[자바스크립트 Deep Dive] 25. 클래스 (ing)

unhyif·2023년 6월 19일
0

25.1 클래스는 프로토타입의 문법적 설탕인가?

  • JS는 class가 필요없는 프로토타입 기반 객체지향 언어임
    • 생성자 함수와 프로토타입을 통해 객체지향 언어의 상속을 구현할 수 있음
function Person(name) {
  this.name = name;
}

Person.prototype.sayHi = function () {
  console.log(`Hi, I'm ${this.name}`);
};

const me = new Person('J');
me.sayHi(); // "Hi, I'm J"
  • class 문법은 기존 프로토타입 기반 패턴을 클래스 기반 패턴처럼 사용할 수 있도록 하는 함수(문법적 설탕)임
  • 클래스와 생성자 함수는 모두 프로토타입 기반의 인스턴스를 생성하지만, 정확히 동일하게 동작하진 않음
    • 클래스는 생성자 함수보다 엄격하며 생성자 함수에선 제공하지 않는 기능도 제공함

class와 생성자 함수의 차이점

  1. 클래스는 반드시 new 연산자와 함께 호출해야 하지만, 생성자 함수는 new 연산자가 없어도 일반 함수로서 호출됨
  2. 생성자 함수와 달리 클래스는 상속을 지원하는 extendssuper 키워드를 제공함
  3. 생성자 함수와 달리 클래스는 호이스팅이 발생하지 않는 것처럼 동작함
  4. 생성자 함수와 달리 클래스는 내부 코드에서 암묵적으로 strict mode가 실행됨
  5. 클래스의 constructor, 프로토타입 메소드, 정적 메소드는 모두 프로퍼티 어트리뷰트 [[Enumerable]] 이 false이기에 열거되지 않음

25.2 클래스 정의

  • 클래스는 일급 객체임
    • 런타임에 생성 가능함
    • 변수나 자료구조에 저장될 수 있음
    • 함수의 매개변수에 전달하거나, 함수의 반환값으로 사용될 수 있음
class Person {}
const Person = class {};
const Person = class Class {};
  • 몸체에는 메소드(constructor, 프로토타입 메소드, 정적 메소드)만 정의할 수 있음
class Person {
  // 인스턴스 생성 및 초기화
  constructor(name) {
    this.name = name; // public
  }

  // 프로토타입 메소드
  sayHi() {
    console.log(`Hi I'm ${this.name}`);
  }

  // 정적 메소드
  static sayHello() {
    console.log(`Hi I'm ${this.name}`);
  }
}

const me = new Person('J');
me.sayHi(); // // "Hi I'm J"
Person.sayHello(); // "Hi I'm Person"

25.3 클래스 호이스팅

  • 클래스 선언문으로 정의한 클래스는 함수 선언문과 같이 런타임 이전에 먼저 평가되어 함수 객체를 생성함
    • 이때 함수 객체는 constructor
    • 생성자 함수와 함께 프로토타입도 생성됨
class Person {}

console.log(typeof Person); // function
  • 클래스 정의 이전에 참조할 수 없어 호이스팅이 발생하지 않는 것처럼 동작함
    • let, const 변수처럼 호이스팅 됨 (TDZ를 가짐)
const Person = '';

{
  console.log(Person); // error
  class Person {}
}

cf) 모든 선언문은 런타임 이전에 실행됨


25.4 인스턴스 생성

const Person = class Class {};

const me = new Person();
console.log(me); // Class {}

25.5 메소드

25.5.1 constructor

  • 인스턴스를 생성하고 초기화하기 위한 특수한 메소드로, 이름을 변경할 수 없음
  • 내부의 this는 클래스가 생성한 인스턴스를 가리킴
    • this에 추가한 프로퍼티는 인스턴스 프로퍼티가 됨
class Person {
  constructor(name) {
    this.name = name;
    this.city = 'seoul';
  }
}

const me = new Person('J');
console.log(me); // Person {name: 'J', city: 'seoul'}
  • 단순한 메소드로 해석되지 않고, 클래스가 평가되어 생성한 함수 객체 코드의 일부가 됨. 즉 constructor에 기술된 동작을 하는 함수 객체가 생성됨

  • 한 개만 존재할 수 있음

  • 생략 가능함

class Person {} // 아래처럼 빈 constructor가 암묵적으로 정의됨

class Person {
  constructor() {}
}
  • 별도의 반환문을 갖지 않아야 함

cf) 프로토타입의 constructor 프로퍼티는 모든 프로토타입이 갖고 있는 프로퍼티로 생성자 함수를 가리키고, 클래스의 constructor 메소드와는 관련이 없음


25.5.2 프로토타입 메소드

  • 클래스에서 정의한 메소드는 기본적으로 프로토타입 메소드임
  • 클래스의 인스턴스는 프로토타입 체인의 일원임
    => 클래스는 프로토타입 기반의 객체 생성 메커니즘임
class Person {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    console.log(`Hi, I'm ${this.name}`);
  }
}

const me = new Person('J');

console.log(Object.getPrototypeOf(me) === Person.prototype); // true
console.log(me instanceof Person); // true

console.log(Object.getPrototypeOf(Person.prototype) === Object.prototype); // true
console.log(me instanceof Object); // true

console.log(me.constructor === Person); // true

25.5.3 정적 메소드

인스턴스를 생성하지 않아도 호출할 수 있는 메소드

  • 클래스는 함수 객체이므로 자신의 프로퍼티/메소드를 소유할 수 있기에, 클래스 정의가 평가된 후 정적 메소드를 호출할 수 있음
  • 인스턴스의 프로토타입 체인엔 클래스가 존재하지 않으므로, 인스턴스는 클래스의 메소드를 상속받을 수 없음
class Person {
  constructor(name) {
    this.name = name;
  }

  static sayHi() {
    console.log('Hi');
  }
}

Person.sayHi(); // Hi
const me = new Person('J');
me.sayHi(); // TypeError: me.sayHi is not a function

25.5.4 정적 메소드와 프로토타입 메소드의 차이

  1. 정적 메소드와 프로토타입 메소드는 자신이 속해 있는 프로토타입 체인이 다름
  2. 정적 메소드는 클래스로 호출하고, 프로토타입 메소드는 인스턴스로 호출함
    -> 정적 메소드와 프로토타입의 메소드 내부의 this 바인딩이 다름
  3. 정적 메소드는 인스턴스 프로퍼티를 참조할 수 없고, 프로토타입 메소드는 참조할 수 있음
    -> 메소드가 인스턴스 프로퍼티를 참조하지 않는다면 정적 메소드로 정의하는 것이 좋음
class Sqaure {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  // 정적 메소드
  static area(width, height) {
    return width * height;
  }

  // 프로토타입 메소드
  area() {
    return this.width * this.height;
  }
}
  • 정적 메소드는 유틸리티 함수들을 구조화할 때 유용함
Reflect.has({ a: 1 }, 'a');

25.5.5 클래스에서 정의한 메소드의 특징

  1. function 키워드를 생략한 축약 표현을 사용함
  2. 메소드 정의 시 콤마가 필요 없음
  3. 암묵적으로 strict mode로 실행됨
  4. 프로퍼티 어트리뷰트 [[Enumerable]] 값이 false라 for in문 또는 Object.keys로 열거할 수 없음
  5. non-constructor이므로 new 연산자와 함께 호출할 수 없음

25.6 클래스의 인스턴스 생성 과정

1. 인스턴스 생성과 this 바인딩

new 연산자와 함께 클래스 호출 시, constructor 실행 이전에 인스턴스(빈 객체)가 생성되고, 인스턴스의 프로토타입으로 클래스의 prototype 객체가 설정됨. 그리고 인스턴스가 this에 바인딩 됨

2. 인스턴스 초기화

constructor가 실행되어 this에 바인딩 된 인스턴스를 초기화 함

3. 인스턴스 반환

완성된 인스턴스가 바인딩 된 this가 암묵적으로 반환됨

class Person {
  constructor(name) {
    console.log(Object.getPrototypeOf(this) === Person.prototype); // true
    this.name = name;
  }
}

25.7 프로퍼티

0개의 댓글