모던 자바스크립트 Deep Dive - 25. 클래스

둡둡·2024년 2월 4일

Modern Javascript Deep Dive

목록 보기
26/49

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

  • 자바스크립트는 프로토타입 기반 객체지향 언어
    • 클래스 없이도 생성자 함수와 프로토타입을 통해 상속 구현 가능
  • ES6 클래스 도입
    • 기존 프로토타입 기반 패턴 -> 클래스 기반 패턴으로 사용
  • 클래스와 생성자 함수의 차이점
      1. 클래스는 new 연산자 없이 호출하면 에러 발생, 생성자 함수는 일반 함수로 호출
      1. 클래스는 상속을 지원하는 extends와 super 키워드 제공
      1. 클래스는 호이스팅이 발생하지 않는 것처럼 동작함, 생성자 함수는 함수 호이스팅과 변수 호이스팅 발생
      1. 클래스는 암묵적으로 strict mode 지정하여 해제 불가
      1. 클래스의 constructor, 프로토타입 메서드, 정적 메서드는 열거되지 않음 ([[Enumerable]] = false)
  • 단순한 문법적 설탕보다는 새로운 객체 생성 메커니즘

25.2. 클래스 정의

  • class 키워드 사용
  • 일반적으로 파스칼 케이스 사용
  • 클래스는 함수, 표현식 사용 가능 = 일급 객체
    • 무명의 리터럴 생성 가능 (런타임 생성)
    • 변수나 자료구조(객체, 배열 등) 저장 가능
    • 함수의 매개변수로 전달 가능
    • 함수의 반환값으로 사용 가능
  • 클래스 몸체에 정의할 수 있는 메서드
    • constructor (생성자)
    • 프로토타입 메서드
    • 정적 메서드
  • 클래스와 생성자 함수의 정의 방식은 형태가 매우 유사함

25.3. 클래스 호이스팅

  • 클래스는 함수로 평가됨
  • 클래스 선언문으로 정의한 클래스는 함수 선언문과 같이 런타임 이전에 평가되어 객체를 생성함
    • 생성된 함수 객체는 constructor
    • 프로토타입도 함께 생성됨
    • 생성자 함수와 프로토타입은 항상 같이 존재함
  • 단, 클래스는 정의 이전에 참조 불가
    • 이때문에 호이스팅이 발생하지 않는 것처럼 보이지만 실제로는 발생함
    • 일시적 사각지대 (temporal dead zone, TDZ)
  • var, let, const, function, function*, class 키워드는 모두 호이스팅 발생

25.4. 인스턴스 생성

  • 클래스는 생성자 함수로, new 연산자와 함께 호출하여 인스턴스 생성
  • 클래스는 인스턴스 생성이 목적이므로 반드시 new 연산자와 함께 호출해야 함
    • new 연산자 없이 호출하면 타입 에러 발생
  • 클래스 표현식으로 정의된 경우 식별자를 사용하면 에러 발생
    • 클래스 이름은 외부 코드에서 접근 불가

25.5. 메서드

  • 클래스 몸체에는 0개 이상의 메서드 선언 가능

25.5.1. constructor

class Person {
  // 생성자
  constructor(name) {
    // 인스턴스 생성 및 초기화
    this.name = name;
  }
}
  • cf. 클래스의 constructor 메서드와 프로토타입의 constructor 프로퍼티는 관련 없음
  • constructor는 인스턴스를 생성 및 초기화하는 특수한 메서드
  • 이름 변경 불가
  • prototype.constructor: 클래스 자신을 가리킴
    • 클래스가 인스턴스를 생성하는 생성자 함수라는 것을 의미
  • constructor는 메서드가 아닌 함수 객체 코드의 일부
    • 클래스 정의가 평가될 때 constructor의 코드로 동작하는 함수 객체 생성
  • 생성자 함수와의 차이점
      1. 클래스 내 하나만 존재: 2개 이상 포함하면 문법 에러(SyntaxError) 발생
      1. constructor 생략 가능: 생략하면 암묵적으로 빈 constructor 정의되어 빈 객체 생성함
      1. 인스턴스 초기화: 고정값 또는 매개변수로 설정 가능
      1. return문 사용 지양: 명시적으로 반환하면 암묵적으로 this 반환이 무시됨
class Person {
  constructor(address) {
    this.name = 'Lee'; // 고정값
    this.address = address // 매개변수 전달
  }
}

const me = new Person('Seoul');
console.log(me); // Person { name: 'Lee', address: 'Seoul' }

25.5.2. 프로토타입 메서드

  • 클래스 메서드는 기본적으로 프로토타입 메서드
  • 클래스가 생성한 인스턴스는 프로토타입 체인이 동일하게 적용됨
    • 프로토타입 메서드를 상속받아 사용할 수 있음
  • 클래스는 생성자 함수와 마찬가지로 프로토타입 기반의 객체 생성 메커니즘

25.5.3. 정적 메서드

  • 정적 메서드는 인스턴스를 생성하지 않아도 호출할 수 있는 메서드
  • 클래스에서는 static 키워드로 정적 메서드 가능
    • static sayHi() { console.log('Hi!') }
  • 정적 메서드는 클래스에 바인딩된 메서드
    • 클래스는 클래스 평가 시점에 함수 객체가 됨
    • 정적 메서드는 클래스 정의 이후 인스턴스 생성 없이 클래스로 호출 가능
      • Person.sayHi(); // Hi!
  • 정적 메서드는 인스턴스로 호출 불가
    • 인스턴스의 프로토타입 체인 상에는 클래스가 존재하지 않기 때문에 상속받을 수 없음

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

    1. 정적 메서드와 프로토타입 메서드는 속해 있는 프로토타입 체인이 다름
    1. 정적 메서드는 클래스로 호출, 프로토타입 메서드는 인스턴스로 호출
    1. 정적 메서드는 인스턴스 프로퍼티 참조 불가, 프로토타입 메서드는 인스턴스 프로퍼티 참조 가능
    • 메서드에서 인스턴스 프로퍼티를 참조해야 한다면 프로토타입 메서드 사용해야 함
  • 메서드 내부의 this는 소유한 객체가 아닌 호출한 객체(마침표 연산자 앞의 객체)에 바인딩
    • 프로토타입 메서드는 인스턴스로 호출 (ex. me.sayHi())
    • 정적 메서드는 클래스로 호출 (ex. Person.sayHi())
    • 따라서 this를 사용하는 메서드는 프로토타입 메서드, 사용하지 않는 메서드는 정적 메서드로 권장
  • 표준 빌트인 객체들은 다양한 정적 메서드를 가지고 있음
    • 애플리케이션 전역에서 사용할 유틸리티 함수
    • 클래스 또는 생성자 함수를 하나의 네임스페이스로 사용하면 구조화 효과

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

    1. function 키워드를 생략한 메서드 축약 표현 사용
    1. 메서드 정의 시 콤마 사용하지 않음
    1. 암묵적으로 strict mode 실행
    1. [[Enumerable]] = false, 열거 불가
    1. non-constructor, new 연산자와 함께 호출 불가

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

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

  • new 연산자 클래스 호출
  • 암묵적으로 빈 객체 인스턴스 생성
  • 인스턴스의 프로토타입 설정 및 this 바인딩

2. 인스턴스 초기화

  • constructor 실행 및 인스턴스 초기화

3. 인스턴스 반환

  • 완성된 인스턴스가 바인딩된 this 반환

25.7. 프로퍼티

25.7.1. 인스턴스 프로퍼티

  • 인스턴스 프로퍼티는 항상 public
  • constructor 내부에서 this에 추가한 프로퍼티는 항상 클래스가 생성한 인스턴스의 프로퍼티가 됨

25.7.2. 접근자 프로퍼티

  • 자체적으로 값을 갖지 않고, 다른 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수로 구성된 프로퍼티
  • getter: get 키워드
    • 인스턴스 프로퍼티에 접근할 때 사용
    • 반환 값 필수
  • setter: set 키워드
    • 인스턴스 프로퍼티에 값을 할당하거나 조작할 때 사용
    • 매개변수 필수

25.7.3. 클래스 필드 정의 제안

  • 클래스 필드는 클래스 기반 객체지향 언어에서 클래스가 생성할 인스턴스의 프로퍼티를 가리킴
  • 자바스크립트의 클래스 몸체에 메서드가 아닌 클래스 필드를 선언하면 문법 에러 발생
    • But, ES10 (ECMA2019) private field 지원
  • this는 constructor와 메서드 내에서만 유효함
  • constructor 클래스 필드를 초기화하지 않으면 undefined 가짐
  • 함수는 일급 객체 -> 클래스 필드에 할당 가능
    • 인스턴스 메서드가 되기 때문에 권장하지 않음
  • 인스턴스 프로퍼티 정의하는 방식
      1. constructor 인스턴스 프로퍼티 정의
      1. 클래스 필드 정의 제안

25.7.4. private 필드 정의 제안

  • ES10 (ECMA2019) private field 지원
  • private 필드 선두 # 키워드 선언 및 참조
    • 타입스크립트 접근 제한자(public, private, protected) 모두 가능
class Person {
  #name = '';
  
  constructor(name) {
    this.#name = name;
  }
}

const me = new Person('Lee');
console.log(me.#name); // SyntaxError
  • 외부에서 직접 접근 불가
    • 접근자 프로퍼티를 통해 간접 접근 가능
  • constructor 안에 private 필드 정의 에러

25.7.5. static 필드 정의 제안

  • ES13 (ECMA2022) static private 필드 및 메서드 도입

25.8. 상속에 의한 클래스 확장

  • 기존 클래스를 상속받아 새로운 클래스를 확장(extends)할 수 있음

25.8.2. extends 키워드

  • 수퍼클래스 = 베이스 클래스 = 부모 클래스: 상속된 상위 클래스
  • 서브클래스 = 파생 클래스 = 자식 클래스: 상속을 통해 확장된 클래스
  • 인스턴스 프로토타입 체인뿐만 아니라 클래스 간 프로토타입 체인도 생성
    • 프로토타입 메서드, 정적 메서드 모두 상속 가능

25.8.3. 동적 상속

  • 클래스뿐만 아니라 생성자 함수 상속 가능
  • [[Constructor]] 내부 메서드를 갖는 모든 표현식 사용 가능
    • 동적으로 상속 대상 결정할 수 있음
  • 단, extends 키워드 앞에는 반드시 클래스가 와야함

25.8.4. 서브클래스의 constructor

  • constructor 생략 시 암묵적으로 비어있는 constructor 정의됨
  • 서브클래스에서는 수퍼클래스의 constructor 호출하여 인스턴스 생성
  • 모두 생략하면 빈 객체 생성됨

25.8.5. super 키워드

  • super 키워드: 함수처럼 호출 가능, this 식별자처럼 참조할 수 있는 특수한 키워드

super 호출

  • 수퍼클래스의 constructor 호출
  • 수퍼클래스의 constructor 그대로 생성하는 경우 서브클래스 생략 가능
  • new 연산자와 함께 전달한 인수를 super 호출을 통해 수퍼클래스로 전달 가능
      1. 서브클래스의 constructor를 생략하지 않는 경우 반드시 super 호출해야 함
      1. 서브클래스의 constructor에서 super 호출하기 전까지는 this 참조 불가
      1. 반드시 서브클래스의 constructor에서만 호출 가능, 이외 에러 발생

super 참조

  • 수퍼클래스의 메서드 호출
  • [[HomeObject]]를 가진 함수만 super 참조 가능
  • ES6의 메서드 축약 표현으로 정의된 함수만 내부 슬롯을 갖고 super 참조 가능
  • 서브클래스 메서드에서 사용

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

1. 서브클래스의 super 호출

  • [[ConstructorKind]]
    • 상속받지 않는 클래스는 'base'
    • 상속받는 서브클래스는 'derived'
  • 서브클래스는 직접 인스턴스 생성하지 않고 수퍼클래스에 위임
    • 반드시 서브클래스의 constructor에서 super 호출하는 이유

2. 수퍼클래스의 인스턴스 생성과 this 바인딩

  • 수퍼클래스가 생성했지만, new.target이 가리키는 서브클래스가 생성한 것으로 처리됨
  • 생성된 인스턴스의 프로토타입은 서브클래스의 프로토타입 객체

3. 수퍼클래스의 인스턴스 초기화

  • 수퍼클래스의 constructor 실행되어 인스턴스 초기화

4. 서브클래스 constructor로의 복귀와 this 바인딩

  • super 호출 종료 및 인스턴스 반환
  • this에 인스턴스 바인딩하여 사용

5. 서브클래스의 인스턴스 초기화

  • 서브클래스의 constructor 실행하여 인스턴스 초기화

6. 인스턴스 반환

  • 완성된 인스턴스가 바인딩된 this 반환

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

  • 표준 빌트인 객체도 extends 키워드 사용하여 확장 가능

    [출처] 모던 자바스크립트, Deep Dive

profile
괴발개발라이프

0개의 댓글