25장: 클래스

중요

클래스는 문법적 설탕?

클래스 기반 언어에 익숙한 프로그래머들에게 프로토타입 기반 프로그래밍에 혼란을 느낀다. (공감)

클래스 도입을 문법적 설탕(=같은기능인데 높은가독성)이라고 많이 표현하는 듯하다. 저자는 새로운 객체 생성 매커니즘이라고 생각한다.

아래 사례들, 클래스가 생성자 함수 기반의 객체 생성 방식보다 간단 명료하다.

사례 1: 기본

  • 정적 메서드

    • 예시

      • Math.random(), Number.isNaN(NaN);

    • 정적 메서드와 프로토타입 메서드는 속해있는 프로토타입 체인이 다르다.

      • 생성된 객체의 프로퍼티 참조 가능 여부 차이

// Before ES6
var Person = (function () {
  // 생성자 함수
  function Person(name) {
    this.name = name;
  }

  // 프로토타입 메서드
  Person.prototype.sayHi = function () {
    console.log('Hi! My name is ' + this.name);
  };

  // 생성자 함수 반환
  return Person;
}());

// 인스턴스 생성
var me = new Person('Lee');
me.sayHi(); // Hi! My name is Lee

// ----------------------------------------
// After ES6
// 클래스 선언문
// 메서드 종류 3가지 : 인스턴스 생성 및 초기화, 프로토타입 메서드, 정적 메서드
class Person {
  constructor(name) {
    this.name = name; // name 프로퍼티는 public하다.
  }

  // prototype 에 메서드를 추가하지 않아도 자동으로 프로토타입 메서드가 된다. 
  sayHi() {
    console.log(`Hi! My name is ${this.name}`);
  }

  // 인스턴스 생성없이도 호출 	가능
  static sayHello() {
    console.log('Hello!');
  }
}

const me = new Person('Lee');
console.log(me.name); // Lee
me.sayHi(); // Hi! My name is Lee
Person.sayHello(); // Hello!

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

사례 2: 상속

  • extend, super 키워드는 상속 관계 구현을 더 간결명료하고 코드 재사용 관점에서 유용.

    • super를 참조하면 수퍼 클래스의 메서드를 호출할 수 있다

    • 생성자함수는 extends와 super 키워드를 지원하지 않음

  • 클래스 뿐만 아니라, 생성자 함수도 상속 가능하다.

  • 서브클래스에서 constructor를 정의하지 않아도, 자동 생성된다.

  • 서브클래스 정적 메서드에서는 부모 정적 메서드만 호출 가능하다.

  • 축약표현으로 정의된 함수만이 [[HomeObject]]를 갖는다.

    • 부모 객체 찾아가기 위한 속성

  • 항상 먼저 super 호출 필요하다.

  • super 로 부모 호출시 부모 생성자에서 this는 생성하려는 자식 인스턴스가 된다.

  • 빌트인 생성자 함수 (Array 등) 확장도 가능하다.

생성자 함수는 클래스와 같이 상속을 통해 다른 생성자 함수를 확장할 수 있는 문법이 제공되지 않는다. 아래코드는 상속 흉내낸 pseudo classical inheritance 패턴

  • 추가적으로, 클래스와 생성자 함수 차이

    • 클래스 암묵적으로 strict mode로 실행된다 (해제 불가), 생성자함수는 strict mode 지정 X

      • 기존에 넘어갔었던 오류가 심각한 버그가 될 경우도 있기에, strict 하게 검사한다. (예전 버전과 compatibility 위해)

    • 클래스는 호이스팅이 발생하지 않는것 처럼 동작한다. 하지만 함수선언문으로 정의된 생성자함수는 함수 호이스팅이, 함수표현식으로 정의한 생성자함수는 변수 호이스팅이 발생한다

      • 변수 호이스팅: 변수의 선언 위치와 상관없이 코드 내 어느 곳에서든 호출이 가능지만 초기화는 undefined

      • 함수 호이스팅: 함수의 선언 위치와 상관없이 코드 내 어느 곳에서든 호출 가능

    • 클래스의 constructor, 프로토타입메서드, 정적메서드는 모두 프로퍼티 속성인 [[Enumerable]]의 값이 false다.

      • for-in 문이나 Object.keys 열거할 수 없다. (propertyIsEnumerable()로 확인 가능)

그냥 보기

클래스 정의 방법

  • 클래스를 표현식으로 표현이 가능한 이유는?

    • 클래스가 값으로 사용할 수 있는 1급 객체

일급 객체?

  • 무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다

  • 변수나 자료구조(배열 등)에 저장할 수 있다

  • 함수 매개변수로 전달 가능하다

  • 함수 반환값으로 사용될 수 있다.

클래스 호이스팅

  • 함수 선언문과 같이 런타임 이전 평가되어 객체를 생성한다.

  • 클래스는 let, const 로 선언한 변수와 같이 호이스팅된다. 따라서 TDZ에 빠지기도 한다.

constructor

construction 는 메서드로 해석되는 것이 아니라, 클래스가 평가되어 생성한 함수객체 코드의 일부가 된다.

constructor 내부에서 return 문은 쓰지 말자 (써도 this 만 반환됨)

접근자 프로퍼티 (get, set)

함수는 일급 객체이므로 함수를 클래스 필드에 함수를 할당가능하다. 다만, 프로토타입 메서드가 아닌 인스턴스 메서드가 되게 된다. 권장하지 않는 방식이라고 저자는 말한다.

상속

클래스가 클래스 뿐만 아니라, 생성자 함수도 상속 받을 수도 있다

동적 상속 (wow 이런게 되네..) 잘쓰이나요? 궁금

서브클래스에서 constructor를 정의하지 않아도, 자동 생성된다.

  • super 동작

    • super를 호출하면 수퍼클래스의 constructor를 호출한다.

    • super를 참조하면 수퍼 클래스의 메서드를 호출할 수 있다

항상 super 먼저 호출

서브클래스는 수퍼클래스에게 인스턴스 생성을 위임한다. 이것이 반드시 super 호출을 필요로 하는 이유다.

super 로 상위 메서드 호출

[[HomeObject]]

축약표현으로 정의된 함수만이 [[HomeObject]]를 갖는다.

정적에서는 정적만

동작 원리

클래스 평가시, 수퍼와 서브 클래스를 나누는 방식 [[ConstructorKind]] 이용 하여, 부모 자식에 각각 "base" 또는 "derived" 값을 넣어준다. 이를통해 new연산자로 호출되었을때의 동작이 구분된다.

super 로 부모 호출시 부모 생성자에서 this는 생성하려는 자식 인스턴스가 된다.

빌트인 생성자 함수 확장 (Array 등)

최신 브라우저에서는 작동하는 코드들

아래 처럼 자바 같이 정의하면 문법에러 발생해야함.

private 필드는 존재하지 않았다.

static키워드를 사용하여 정적필드를 정의할 수는 없었다.

Last updated