TIL. Class선언과 표현식

서동이·2021년 8월 10일
0

Classes


클래스는 객체를 생성하기 위한 템플릿이다.
클래스는 데이터이를 조작하는 코드를 하나로 추상화한다. 자바스크립트에서 클래스는 프로토타입을 이용해서 만들어졌지만 ES5의 클래스 의미와는 다른 문법과 의미를 가집니다.

Class 정의

Class는 사실 "특별한 함수"이다.
함수를 함수 표현식과 함수 선언으로 정의할 수 있듯이
class 문법도 class 표현식 and class 선언 두 가지 방법을 제공한다.

**함수: Function생성자로부터 생성되는 객체이다. 다른 객체들과 다르게 호출할수 있다는 특징이 있다
출처

Class 선언

Class를 정의하는 한 가지 방법은 class 선언을 이용하는 것이다. class를 선언하기 위해서는 클래스의 이름(여기서 "Rectangle")과 함께 class 키워드를 사용해야 한다.


class Rectangle {
    constructor(widht, height){
       this.height = height;
       this.width = width;
	}
}

Hoisting

함수 선언과 클래스 선언의 중요한 차이점은
함수 선언의 경우 호이스팅이 일어나지만,
클래스 선언은 그렇지 않다는 것이다.

클래스를 사용하기 위해서는 클래스를 먼저 선언 해야 한다. 그렇지 않으면, 다음 코드는 ReferenceError를 던진다.

const p = new Rectangle(); // ReferenceError

class Rectangle {}

class 표현식

Class 표현식은 class를 정의하는 또 다른 방법이다.
Class 표현식은 이름을 가질 수도 있고, 갖지 않을 수도 있다. 이름을 가진 class 표현식의 이름은 클래스 body의 local scope에 한해 유효합니다. (하지만, 클래스의 (인스턴스 이름이 아닌) name 속성을 통해 찾을 수 있습니다).

// unnamed(이름 없는 class)
let Rectangle = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// 출력: "Rectangle"

// named(이름 있는 class)
let Rectangle = class Rectangle2 {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// 출력: "Rectangle2"

Class body(본문)와 메서드 정의

Class body는 중괄호 {} 로 묶여 있는 안쪽 부분.
이곳은 메서드나 constructor와 같은 class 멤버를 정의할 곳이다.

특징 1> Strict mode

클래스의 본문(body)은 strict mode에서 실행된다.
즉, 여기에 적힌 코드는 성능 향상을 위해 더 엄격한 문법이 적용된다.
그렇지 않으면, 조용히 오류가 발생할 수 있습니다. 특정 키워드는 미래의 ECMAScript 버전용으로 예약됩니다.

특징 2> Constructor (생성자)

constructor 메서드는 class 로 생성된 객체를 생성하고 초기화하기 위한 특수한 메서드이다. "constructor" 라는 이름을 가진 특수한 메서드는 클래스 안에 한 개만 존재할 수 있다.
만약 클래스에 여러 개의 constructor 메서드가 존재하면 SyntaxError 가 발생할 것이다.

constructor는 부모 클래스의 constructor를 호출하기 위해 super 키워드를 사용할 수 있다.

특징 3> 프로토타입 메서드

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  // Getter
  get area() {
    return this.calcArea();
  }
  // 메서드
  calcArea() {   //단축구문은 익명(anonymous)함수 대신 유명(named)함수를 사용
    return this.height * this.width; 
  }  //유명함수는 참조할 식별자가 있기 때문에 함수 본체에서 호출 가능
}

const square = new Rectangle(10, 10);

console.log(square.area); // 100

1) 정적 메서드와 속성

  • static 키워드는 클래스를 위한 정적(static) 메서드를 정의한다.
  • 클래스의 인스턴스화(instantiating) 없이 호출되며, 클래스의 인스턴스에서는 호출할 수 없다.
  • 어플리케이션(application)을 위한 유틸리티(utility) 함수를 생성하는 데 주로 사용된다.
    -캐시, 고정 환경설정 또는 인스턴스 간에 복제할 필요가 없는 기타 데이터에 유용하다.
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  static displayName = "Point"; //고정된 환경설정 또는 인스턴스간 복제필요가 없을때 유용
  static distance(a, b) {
    const dx = a.x - b.x;  
    const dy = a.y - b.y;  //수식은 바뀌지 않음. 고정된 환경설정임. 따라서 정적속성사용

    return Math.hypot(dx, dy);
  }
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
p1.displayName; // undefined
p1.distance;    // undefined
p2.displayName; // undefined
p2.distance;    // undefined

console.log(Point.displayName);      // "Point"
console.log(Point.distance(p1, p2)); // 7.0710678118654755

2) 프로토타입 및 정적 메서드를 사용한 this바인딩
메서드를 변수에 할당 한 다음 호출하는 것과 같이, 정적 메서드나 프로토타입 메서드가 this 값 없이 호출될 때, this 값은 메서드 안에서 undefined가 된다.
이 동작은 "use strict" 명령어 없이도 같은 방식으로 동작하는데,
class 문법 안에 있는 코드는 항상 strict mode 로 실행되기 때문.

class Animal {
 speak() {
   return this;
 }
 static eat() {
   return this;
 }
}

let obj = new Animal();
obj.speak(); // the Animal object
let speak = obj.speak;
speak(); // undefined

Animal.eat() // class Animal
let eat = Animal.eat;
eat(); // undefined

위에 작성된 코드를 전통적 방식의 함수기반의 non–strict mode 구문으로 재작성하면,
this 메서드 호출은 기본적으로 전역 객체인 초기 this 값에 자동으로 바인딩 됩니다.
Strict mode에서는 자동 바인딩이 발생하지 않습니다; this 값은 전달된 대로 유지됩니다.

function Animal() { }

Animal.prototype.speak = function() {
  return this;
}

Animal.eat = function() {
  return this;
}

let obj = new Animal();
let speak = obj.speak;
speak(); // global object (in non–strict mode)

let eat = Animal.eat;
eat(); // global object (in non-strict mode)

3) 인스턴스 속성
인스턴스 속성은 반드시 클래스 메서드 내에 정의되어야 한다.

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

정적 (클래스사이드) 속성과 프로토타입 데이터 속성은
반드시 클래스 선언부 바깥쪽에서 정의되어야 한다.

Rectangle.staticWidth = 20;
Rectangle.prototype.prototypeWidth = 25;

4) Field선언

  • public 필드 선언
    :필드를 먼저 선언함으로서 클래스 정의는 self-documenting에 가까워졌고
    필드는 언제나 존재하는 상태가 된다.
    class Rectangle {
    height = 0;
    width;
    constructor(height, width) {
      this.height = height;
      this.width = width;
    }
    }
- private 필드 선언

 ```jsx
class Rectangle {
  #height = 0;
  #width;
  constructor(height, width) {
    this.#height = height;
    this.#width = width;
  }
}

클래스의 바깥에서 private 필드를 접근하려고 하면 에러가 발생한다.
private필드는 클래스 내부에서만 읽고 쓰기가 가능하다.
클래스 외부에서 보이지 않도록 정의하였으므로 클래스가 버젼업 되면서 내부 구현이 바뀌더라도
클래스 사용자 입장에서는 이에 아무런 영항을 받지 않도록 할 수 있다.

  • Private 필드는 사용전에 선언되어야 합니다.
  • 일반적인 프로퍼티와는 다르게 private 필드는 값을 할당하면서 만들어질 수 없다.

특징 4> extends를 통한 클래스 상속(sub classing)

extends 키워드는 클래스 선언이나 클래스 표현식에서
다른 클래스의 자식 클래스를 생성하기 위해 사용된다.

//부모

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

//자식

class Dog extends Animal {
  constructor(name) {
    super(name); // super class 생성자를 호출하여 name 매개변수 전달
  }
//subclass에 constructor가 있다면,
// "this"를 사용하기 전에 가장 먼저 super()를 호출해야 한다.
  
  speak() {
    console.log(`${this.name} barks.`);
  }
}

let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.
//클래스는 생성자가 없는 객체(non-constructible)을 확장할 수 없다.
// 만약 기존의 생성자가 없는 객체을 확장하고 싶다면,Object.setPrototypeOf() 메서드를 사용.

const Animal = {
  speak() {
    console.log(`${this.name} makes a noise.`);
  }
};

class Dog {
  constructor(name) {
    this.name = name;
  }
}

// 이 작업을 수행하지 않으면 speak를 호출할 때 TypeError가 발생
Object.setPrototypeOf(Dog.prototype, Animal);

let d = new Dog('Mitzie');
d.speak(); // Mitzie makes a noise.

특징 5> Species

class MyArray extends Array {  //배열을 상속 받은 MyArray 클래스
  // 부모 Array 생성자로 species 덮어쓰기(Array Object를 반환)
  static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x); //기본생성자를 반환하는 map메서드. 이 메서드가 myArray객체대신 array객체를 반환하게 할때 Symbol.species 를 사용하여 이를 가능하게 함.

console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array);   // true

특징 6> super를 통한 상위 클래스 호출

super 키워드는 객체의 부모가 가지고 있는 메서드를 호출하기 위해 사용됨.
이는 프로토타입 기반 상속보다 좋은 점 중 하나입니다.

(메서드 상속 vs. 프로토타입 상속)

class Cat {
 constructor(name) {
   this.name = name;
 }

 speak() {
   console.log(`${this.name} makes a noise.`);
 }
}

class Lion extends Cat {
 speak() {
   super.speak();
   console.log(`${this.name} roars.`);
 }
}

let l = new Lion('Fuzzy');
l.speak();
// Fuzzy makes a noise.
// Fuzzy roars.

특징 7> Mix-ins

추상 서브 클래스 또는 믹스-인은 클래스를 위한 템플릿이다.
ECMAScript 클래스는 하나의 단일 슈퍼클래스만을 가질 수 있다.
예를 들어 툴링 클래스로부터의 다중 상속은 불가능하다.
기능은 반드시 슈퍼클래스에서 제공되어야 합니다.

슈퍼클래스를 인자로 받고 이 슈퍼클래스를 확장하는 서브클래스를 생성하여 반환하는 함수를 사용하여 ECMAScript에서 믹스-인을 구현할 수 있다:

var calculatorMixin = Base => class extends Base {
  calc() { }
};

var randomizerMixin = Base => class extends Base {
  randomize() { }
};
Copy to Clipboard
위 믹스-인을 사용하는 클래스는 다음과 같이 작성할 수 있습니다:

class Foo { }
class Bar extends calculatorMixin(randomizerMixin(Foo)) { }

클래스 재정의

클래스는 재정의될 수 없다. 재정의를 시도하면 SyntaxError 가 발생한다.

출처 :https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Classes

profile
오늘도 여전히 사고친걸 해결해본당,,

0개의 댓글