본문 바로가기
Books/모던 JS Deep Dive ✔️

[모던 JS Deep Dive] 25장 - 클래스

by Aaron-Kim 2022. 1. 10.

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

  - JS는 프로토타입 기반의 객체 지향 언어 (클래스 필요없는, class free)

    - 과거 ES5 생성자 함수와 프로토타입을 이용하여 객체지향 언어의 상속 구현 가능했음

  - 클래스는 사실 함수임, 기존 프로토타입 기반 패턴을 클래스 기반 패턴처럼 사용 가능한

    문법적 설탕 (Syntactic sugar)이라고도 볼 수 있음 => 새로운 객체 생성 메커니즘으로 보는 것이 적절

  - 클래스와 생성자 함수 모두 프로토타입 기반 인스턴스 생성, 하지만 정확히 동일하게 동작하지 않음

    - 클래스는 생성자 함수보다 엄격하고, 추가 기능 제공

    - ES6 클래스; extends, super 키워드 제공, new 연산자 없이 호출하면 에러 발생,

                      클래스 호이스팅 발생하지 않는 것 처럼 동작, 클래스 내 모든 코드에 암묵적 strict mode 적용,

                      클래스의 모든 멤버 변수와 함수는 열거 불가

25.2 클래스 정의

  - class 키워드 사용해서 정의 (파스칼 케이스)

  - 사실 클래스도 표현식으로 정의할 수 있음 (변수에 할당 가능, 익명/기명 클래스 표현식)

    - 클래스는 값처럼 사용할 수 있는 '일급 객체'

  - 클래스 몸체에서 정의할 수 있는 3가지 메서드

    - constructor (생성자)

    - 프로토타입 메서드

    - 정적 메서드

25.3 클래스 호이스팅

  - 클래스는 함수로 평가됨

  - 런타임 이전에 먼저 평가되어 함수 객체 생성 (constructor, 생성자 함수로서 호출 가능)

    - 프로토타입도 더불어 생성됨

    - 프로토타입과 생성자 함수는 항상 쌍으로 존재

  - 클래스는 정의 이전에 참조 불가

    - 클래스는 호이스팅이 발생하지 않는 것 처럼 보임

    - 클래스는 let, const 키워드로 선언한 변수처럼 호이스팅됨 (TDZ)

    - 모든 식별자는 호이스팅됨 (모든 선언문은 런타임 이전에 먼저 평가되기 때문)

25.4 인스턴스 생성

  - 클래스는 생성자 함수, new 연산자와 함께 호출되어 인스턴스 생성

    - new 연산자 없이 호출하면 TypeError 발생

25.5 메서드

  - constructor

    - 인스턴스를 생성하고 초기화하기 위한 특수한 메서드 (이름 변경 불가)

    - 클래스도 함수 객체 고유의 프로퍼티 모두 가지고 있음

    - constructor 내부의 this는 클래스가 생성한 인스턴스 가리킴

    - 클래스의 constructor와 프로토타입의 constructor 프로퍼티는 관련 없음,

      프로토타입의 constructor 프로퍼티는 모든 프로토타입이 가지고 있는 프로퍼티, 생성자 함수 가리킴

    - 클래스 내에 constructor는 최대 한 개만 존재 가능 (초과 시 SyntaxError 발생)

      - constructor는 생략 가능 (빈 객체가 암묵적으로 정의됨)

    - constructor 에는 return문 없어야 함 (암묵적으로 this (인스턴스) 반환함)

  - 프로토타입 메서드

    - 생성자 함수처럼 굳이 '생성자함수명.prototype.메서드' 이렇게 설정할 필요 없음

      - 메서드 이름만 가지고 바로 선언해도 기본적으로 프로토타입 메서드가 됨

      - 클래스가 생성한 인스턴스는 프로토타입 체인의 일원이 됨

      - 인스턴스는 프로토타입 메서드 상속받아 사용 가능

  - 정적 메서드

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

    - 클래스 내에 메서드 앞에 static 키워드 붙이기 (클래스 메서드)

    - 클래스에 바인딩된 메서드가 됨

    - 클래스로 호출

    - 정적 메서드는 인스턴스로 호출 불가

      - 정적 메서드가 바인딩된 클래스는 인스턴스의 프로토타입 체인상에 존재하지 않음

  - 정적 메서드와 프로토타입 메서드의 차이

    - 프로토타입 체인이 다름

    - 정적 메서드는 클래스로 호출하고, 프로토타입 메서드는 인스턴스로 호출함

    - 정적 메서드는 인스턴스 프로퍼티 참조 불가, 프로토타입 메서드는 인스턴스 프로퍼티 참조 가능

      - 정적 메서드 내에서 this 키워드 사용 X

      - 메서드 내에서의 this는 메서드를 소유한 객체가 아니라 호출한 객체에 바인딩됨 (this 바인딩 다름)

        - 프로토타입 메서드는 인스턴스로 호출하므로 this는 인스턴스 가리킴

        - 정적 메서드는 클래스로 호출해야 하므로 this는 클래스를 가리킴

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

    - function 키워드 생략한 메서드 축약 표현 사용

    - 객체 리터럴과 다르게 콤마 사용 필요 X

    - 암묵적으로 strict mode 사용

    - for ... in 문이나 Object.keys()로 열거 불가

    - non-construtor, 메서드는 new 연산자로 호출 불가

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

  - new 연산자와 함께 클래스 호출하면 클래스의 내부 메서드 [[Construct]]가 호출됨

  - 인스턴스 생성과 this 바인딩

    - new 연산자로 클래스 호출 시 constructor 내부 코드 실행 전에,

      암묵적으로 빈 객체 생성됨 (클래스가 생성한 인스턴스)

    - 클래스가 생성한 인스턴스의 프로토타입으로 클래스의 prototype 프로퍼티가 가리키는 객체가 설정됨

    - 인스턴스는 this에 바인딩됨

  - 인스턴스 초기화

    - this에 바인딩되어 있는 인스턴스 초기화 (프로퍼티 추가, 매개값으로 받은 것을 할당하여 초기화)

  - 인스턴스 반환

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

25.7 프로퍼티

  - 인스턴스 프로퍼티

    - constructor 내부에서 정의해야 함

    - JS에서는 접근 제한자 지원 X

    - 기본적으로 인스턴스 프로퍼티는 언제나 public

  - 접근자 프로퍼티

    - 자체적으로 값([[Value]] 내부 슬롯)을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나,

      저장할 때 사용하는 접근자 함수로 구성된 프로퍼티

    - 접근자 프로퍼티는 get, set, enumerable, configurable 프로퍼티 어트리뷰트 가짐

    - get 함수이름() {}, set 함수이름() {}

    - getter/setter 함수

      - 함수 이름이 인스턴스 프로퍼티처럼 사용됨 (호출하는 방식 아님)

      - 클래스의 접근자 프로퍼티 또한 인스턴스 프로퍼티가 아닌 프로토타입의 프로퍼티가 됨

  - 클래스 필드 정의 제안

    - 클래스가 생성할 인스턴스의 프로퍼티를 가리키는 용어 (필드, 멤버)

    - Class field declarations

      - 인스턴스 프로퍼티를 마치 클래스 기반 언어의 필드처럼 정의 가능

      - this 사용하면 안됨 (this는 constructor와 메서드 내에선만 사용 가능)

      - 함수는 일급 객체이므로 클래스 필드에 할당 가능 (화살표 함수, 함수 표현식 가능),

        이 함수는 프로토타입 메서드가 아닌 인스턴스 메서드가 됨 (권장 X)

      - 모든 클래스 필드는 인스턴스 프로퍼티가 됨

      - 클래스 필드에 화살표 함수 할당하여 화살표 함수 내부의 this가 인스턴스 가리키게 할 수 있음

        - 이벤트 핸들러 내부의 this는 DOM 요소를 가리킴

        - 화살표 함수 내부의 this는 항상 상위 컨텍스트의 this를 가리킴

  - private 필드 정의 제안

    - 인스턴스 프로퍼티는 인스턴스를 통해 클래스 외부에서 언제나 참조 가능 (public)

    - 클래스 필드도 기본적으로 public

    - private 필드 앞에 # 붙이면 private로 적용됨 (사용할 때도 # 붙이면서 해야 함)

      - 클래스 내부에서만 참조 가능 (클래스 외부에서 참조 불가)

      - 접근자 프로퍼티를 통해 간접적으로 접근은 가능

      - private 필드는 constructor에 정의하면 SyntaxError 발생, 클래스 몸체에 정의해야 함

    - 타입스크립트는 접근 제한자 모두 지원 (JS의 superset)

  - static 필드 정의 제안

    - static public 필드 (static 키워드 이용)

    - static private 필드 (static 키워드와 # 이용)

25.8 상속에 의한 클래스 확장

  - 클래스 상속과 생성자 함수 상속

    - 생성자 함수 상속; 프로토타입 기반 상속은 프로토타입 체인 통해 다른 객체의 자산 상속받는 개념

      - 생성자 함수는 상속을 통해 다른 생성자 함수 확장하는 문법 없음

      - 과거 pseudo classical inheritance 패턴 있긴 했음

    - 클래스 상속; 기존 클래스를 상속받아 새로운 클래스 extends (확장)하여 정의

      - 상속에 의한 클래스 확장은 코드 재사용 관점에서 매우 유용

  - extends 키워드

    - extends 키워드 우측에 상속 받을 클래스 정의

    - Super/Base/Parent 클래스 - Sub/Derived/Child 클래스

    - 클래스도 프로토타입 통해 상속 관계 구현

    - 클래스 간 프로토타입 체인, 인스턴스 간 프로토타입 체인 모두 생성됨,

      => 프로토타입 메서드, 정적 메서드 모두 상속 가능

  - 동적 상속

    - 클래스가 생성자 함수 상속받아 클래스 확장 가능 (extends 키워드 사용)

    - 클래스 extends [[Construct]] 내부 메서드 갖는 함수 객체로 평가될 수 있는 모든 표현식 사용 가능

      - 동적으로 상속받을 대상 결정 가능

  - sub 클래스의 constructor

    - 클래스에서 constructor 생략하면 비어 있는 constructor 암묵적으로 정의됨

    - sub 클래스에서 constructor 생략하면, constructor(...args) { super(...args}; } 암묵적으로 정의됨

    - super 클래스와 sub 클래스 모두 constructor 생략하면 빈 객체 생성됨

  - super 키워드

    - super 키워드는 함수처럼 호출 가능, 식별자처럼 참조도 가능

    - super 호출

      - super 호출 시, super 클래스의 constructor(super-constructor) 호출

      - sub 클래스에서 constructor 생략 안하면 반드시 super 호출해야 함

      - sub 클래스의 constructor에서 super 호출 전에는 this 참조 불가

      - super는 반드시 sub 클래스의 constructor에서만 호출

    - super 참조

      - super 참조 시, super 클래스의 메서드 호출 가능

      - ES6 축약 메서드는 내부 슬롯 [[HomeObject]] 가지며, 자신을 바인딩하고 있는 객체 가리킴

      - [[Home Object]] 가지는 sub 클래스의 메서드에서 super 참조 가능

      - 객체 리터럴에서도 super 참조 가능 (ES6 메서드 축약만 가능)

      - sub 클래스의 정적 메서드에서 super.정적메서드 은 super 클래스의 동일한 이름의 정적 메서드 가리킴

  - 상속 클래스의 인스턴스 생성 과정

    - sub 클래스의 super 호출

      - JS 엔진은 super 클래스와 sub 클래스 구분 위해 base 또는 derived 값 갖는 내부 슬롯 [[ConstructorKind]] 가짐

      - sub 클래스는 자신이 직접 인스턴스 생성하지 않고 super 클래스에게 인스턴스 생성 위임

      - sub 클래스 constructor에서 반드시 super를 호출해야 함

    - super 클래스의 인스턴스 생성과 this 바인딩

      - super 클래스 constructor 내부 코드 실행 전에 암묵적으로 빈 객체 생성

      - 이 빈 객체가 아직 미완성된 인스턴스임, this에 바인딩됨

      - new.target은 new 연산자와 함께 호출된 서브 클래스를 따라감 (비록 super 클래스에서 만들었지만)

      - 생성된 인스턴스의 프로토타입은 서브 클래스의 prototype 프로퍼티가 가리키는 객체임

    - super 클래스의 인스턴스 초기화

    - sub 클래스 constructor로의 복귀와 this 바인딩

      - super가 반환한 인스턴스가 this에 바인딩됨

      - sub 클래스는 별도의 인스턴스 생성하지 않고 super가 반환한 인스턴스를 this에 바인딩하여 그대로 사용

    - sub 클래스의 인스턴스 초기화

    - 인스턴스 반환

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

  - 표준 빌트인 생성자 함수 확장

    - String, Number, Array 같은 표준 빌트인 객체도 [[Construct]] 내부 메서드 갖는 생성자 함수이므로

      extends 키워드 사용하여 확장 가능

    - 나만의 원하는 메서드 만들어서 활용 가능

반응형

댓글