본문 바로가기
개발서적/모던 자바스크립트 Deep Dive

클래스 (모던 자바스크립트 Deep Dive)

by 사과넹 2024. 4. 17.
반응형

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

  • 자바스크립트에서 클래스는 함수이다.
  • 기존 프로토타입 기반 패턴을 클래스 기반 패턴처럼 사용할 수 있도록하는 새로운 객체 생성 메커니즘이다.
  • 생성자 함수와 유사하나 차이는 있다.
    • 꼭 new 연산자가 필요하다.
    • 상속을 지원하는 extends와 super 키워드를 제공한다.
    • 호이스팅이 발생하지 않는 것처럼 동작한다.
    • 클래스 내부 코드는 무조건 strict mode가 지정된다.
    • 열거되지 않는다.

 

25.2 클래스 정의

  • class 키워드를 사용하며 일반적으로 파스칼 케이스를 사용한다.
  • 클래스는 함수이기 때문에 일급 객체이다. 그러므로 일급 객체의 특징을 가진다.
    • 무명의 리터럴로 생성할 수 있고, 런타임에 생성이 가능하다.
    • 변수나 자료구조에 저장할 수 있다.
    • 함수의 매개변수에 전달할 수 있다.
    • 함수의 반환값으로 사용할 수 있다.
  • 클래스 몸체에는 0개 이상의 메서드만 정의할 수 있다.
    • constructor
    • 프로토타입 메서드
    • 정적 메서드
class Person {
	// 생성자
	constructor(name) {
		this.name = name;
	}
	
	// 프로토타입 메서드
	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!
  • 생성자 함수의 정의 방식이 유사하나 다르다.

 

25.3 클래스 호이스팅

  • 클래스는 함수이기 때문에 평가 과정에서 함수 객체를 생성한다.
    • 또한 호이스팅이 발생하지만 정의 이전에 사용될 수 없으며 let과 const와 동일하게 동작하여 TDZ 현상으로 인해 호이스팅이 발생하지 않는 것 처럼 보인다.
  • constructor를 가진 함수 객체로 생성되며 프로토타입도 함께 생성된다.

 

25.4 인스턴스 생성

  • 생성자 함수처럼 new 키워드로 인스턴스를 생성한다.
    • new 없이 생성시 에러 발생
  • 기명 클래스 표현식의 이름을 통해서 인스턴스를 생성할 수 없다.
    • 인스턴스 생성시엔 클래스 식별자를 이용해야한다.
    • 기명 클래스의 이름은 클래스 내부에서만 유효하다.

 

25.5 메서드

  • 생성 가능한 메서드
    • constructor
    • 프로토타입 메서드
    • 정적 메서드

인스턴스 프로퍼티는 클래스 constructor 내부에서 정의되어야만 했지만 클래스 몸체 내부에서도 정의 가능하게 하는 표준 사양이 제시되었다.

 

25.5.1 constructor

  • 인스턴스 생성 및 초기화
  • 여기서 가리키는 this 는 클래스가 생성한 인스턴스이다.
  • constructor는 클래스 내 메서드로 해석되는 것이 아닌 함수 객체 코드의 일부가 된다.
    • 그렇기 때문에 클래스가 생성한 인스턴스에서는 constructor라는 메서드를 볼 수 없다.
  • 이는 클래스 내부에서 한 개만 존재하며 생략해도 암묵적으로 정의가 된다.
  • 인스턴스 프로퍼티 초기값은 클래스의 매개변수를 통해 전달된다.
    • 이는 constructor의 매개변수로 전달된다.
  • 또한 constructor 내부 return문은 사용하지 말아야한다.
    • 임의의 객체 또는 원시값을 반환하게 되면 암묵적인 this의 반환이 무시된다.

 

25.5.2 프로토타입 메서드

  • 생성자 함수처럼 프로토타입에 메서드를 추가해야하지만 클래스에서는 기본적으로 프로토타입 메서드로 추가된다.
  • 동일하게 클래스가 생성한 인스턴스도 프로토타입 체인의 일원이다.

 

25.5.3 정적 메서드

  • 클래스 내부 메서드 앞에 static 키워드를 붙이면 클래스의 정적 메서드가 된다.
    • 정적 메서드도 프로토타입 체인에 포함되는데 해당 클래스 밑으로 들어가게 되고, 클래스의 프로토타입 밑으로 들어가지 않는다. → 상속되지 않는다.
  • 이는 클래스로 호출이 가능하다.

 

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

  • 두 메서드의 차이는 아래와 같다.
    • 자신이 속해 있는 프로토타입 체인이 다르다.
    • 정적 메서드는 클래스를 통해, 프로토타입 메서드는 인스턴스를 통해 호출한다.
    • 정적 메서드는 인스턴스 프로퍼티를 참조할 수 없고, 프로토타입 메서드는 가능하다.
  • 메서드 내부에 인스턴스 프로퍼티를 참조해야 한다면 this를 사용해야하고 그럴 필요 없다면 사용하지 않아도 된다.
  • 표준 빌트인 객체인 Math, Number, JSON, Object, Reflect 등은 다양한 정적 메서드를 가지고 있고, 애플리케이션 전역에서 사용할 유틸리티 함수다.

 

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

  1. 인스턴스 생성과 this 바인딩
    • new 연산자와 클래스를 호출하면 constructor가 실행되기 앞서 빈 객체가 생성된다.
    • 프로토타입 프로토퍼티가 가리키는 객체가 설정되고 생성된 빈 객체 인스턴스가 this에 바인딩 된다.
  2. 인스턴스 초기화
    • constructor 내부 코드가 실행되어 this에 바인딩되어 있는 인스턴스를 초기화한다.
    • 인스턴스에 프로퍼티를 추가하고 인수를 전달받았다면 해당 값으로 초기화된다.
    • constructor가 없으면 생략된다.
  3. 인스턴스 반환
    • 바인딩된 this가 암묵적으로 반환된다.

 

25.7 프로퍼티

25.7.1 인스턴스 프로퍼티

  • constructor내부의 this로 추가한 프로퍼티는 항상 public한 상태이다.

 

25.7.2 접근자 프로퍼티

  • 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수
    • getter과 setter 함수

 

25.7.3 클래스 필드 정의 제안

  • 클래스기반 객체 지향 언어에서 클래스 필드는 클래스가 생성할 인스턴스의 프로퍼티를 가리킨다.
    • 자바스크립트에서 인스턴스의 프로퍼티는 constructor내부에서만 정의할 수 있다.
    • 자바스크립트의 클래스 몸체에서는 메서드만 정의할 수 있다.
  • 하지만 최신 ECMAScript 사양에 클래스 몸체에서 프로퍼티를 정의할 수 있게 되었다.
    • Babel과 같은 빌드 시스템을 이용하면 사용할 수 있다.
  • 이때 클래스의 몸체에서 클래스 필드를 정의할 수 있는 클래스 필드 정의 를 할 때는 this 를 사용하면 안된다.
    • 하지만 참조를 할 때는 반드시 this 를 사용해야한다.
  • 함수를 클래스 필드에서 할당할 수 있다.
    • 이 함수는 프로토타입 메서드가아닌 인스턴스 메서드가 되기 때문에 권장되지 않는다.
  • 외부 초기값으로 클래스 필드를 초기화할 필요가 있다면 constructor에서 정의해야한다.
    • 클래스 필드에서 정의한 방식은 외부 초기값으로 초기화가 불가능하다.

 

25.7 private 필드 정의 제안

  • 자바스크립트에서 캡슐화는 완전하게 지원하지 않는다.
    • 하지만 최신 문법에서 표준으로 제안되어 구현이 가능한 상태이다.
  • private 필드의 선두에 #을 붙여준다.
class Person {
  // private 필드 정의
	#name = '';
	
	constructor(name) {
		this.#name = name;
	}
}

const me = new Person('Lee');

console.log(me.#name); // SyntaxError :: 클래스 외부에서 접근 불가
  • 접근자 프로퍼티를 통해서는 간접 접근 가능하다.
    • getter

 

25.7.5 static 필드 정의 제안

  • 이 또한 최신 문법 표준에서 제안되어 구현이 가능한 상태다.
  • static을 통해 정적 메서드 뿐만 아니라 정적 프로퍼티를 정의할 수 있다.

 

25.8 상속에 의한 클래스 확장

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

  • 기존 클래스를 상속받아 새로운 클래스를 확장하여 정의하는 것
    • extends 키워드를 활용하여 상속받은 클래스를 확장한다.

 

25.8.2 extends 키워드

// 수퍼(베이스/부모) 클래스
class Base {}

// 서브(파생/자식) 클래스
class Derived extends Base {}
  • extends 키워드를 통해 수퍼클래서와 서브클래스 간의 상속 관계를 설정한다.
  • 인스턴스의 프로토타입 체인과 클래스 간의 프로토타입 체인도 생성한다.
    • 프로로타입 메서드, 정적 메서드 모두 상속한다.

 

25.8.3 동적 상속

  • 생성자 함수도 상속 받을 수 있다.
  • extends 다음에는 모든 표현식을 상속 받을 수 있다.
    • 조건식을 통해 동적으로 상속받을 대상을 결정할 수 있다.
function Base1() {}

class Base2 {}

let condition = true;

// condition이 true니까 Base1을 상속받는다.
// 또한 extends 좌항에는 클래스만 올 수 있다. 우항에는 생성자함수와 클래스 둘 다 올 수 있다.
class Derived extends (condition ? Base1 : Base2) {}

 

25.8.4 서브클래스의 constructor

  • 클래스에서 constructor 을 생략하면 암묵적으로 정의된다.
  • 서브클래스에서도 동일하게 동작한다.

 

25.8.5 super 키워드

  • super를 호출하면 수퍼클래스의 constructor을 호출한다.
    • 서브클래스에서 constructor을 정의하지 않았더라도 암묵적으로 constructor가 정의되고 super가 호출된다.
    • 서브클래스에서 수퍼클래스에서 생성한 프로퍼티를 갖는 인스턴스를 생성하면 constructor을 반드시 정의해야하고 super를 통해 인수를 전달한다.
    • 주의 사항
      • 서브클래스에서 constructor을 생략하지 않는다면 반드시 super을 호출해야한다.
      • 서브클래스의 constructor에서 super을 호출하기 전에는 this를 참조할 수 없다.
      • super은 서브클래스에서만 호출할 수 있다.
  • super을 참조하면 수퍼클래스의 메서드를 호출할 수 있다.
    • super 참조를 통해 수퍼클래스의 메서드를 참조하려면 수퍼클래스의 프로토타입 프로퍼티에 바인딩된 프로토타입을 참조할 수 있어야 한다.
    • 참조가 동작하기 위해서는 super를 참조하고 있는 메서드가 바인딩되어 있는 객체의 프로토타입을 찾을 수 있어야하기 때문에 메서드는 내부 슬롯 [[HomeObject]]를 가지고 자신이 바인딩하고 있는 객체를 가리킨다.
      • 해당 내부슬롯이 있어야 super를 참조할 수 있다.
      • 함수 축약 표현으로 정의된 함수만 super 참조를 할 수 있다.
const obj = {
	foo() {}, // [[HomeObject]] 있음

	bar: function () {} // [[HomeObject]] 없음
};

 

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

  1. 서브클래스의 super 호출
    • 자바스크립트 엔진에서 클래스를 평가할 때, 수퍼클래스와 서브클래스를 구분하기 위해 클래스들은 내부 슬롯 [[ConstructorKind]]를 갖는다.
      • 수퍼클래스 : 상속 받지않은 클래스 base
      • 서브클래스 : 상속 받은 클래스 derived
    • 서브클래스는 자신이 직접 인스턴스를 생성하지 않고 수퍼클래스에게 이 과정을 위임한다.
      • 그렇기 때문에 서브클래스의 constructor에서 super을 호출해야만 한다.
  2. 수퍼클래스의 인스턴스 생성과 this 바인딩
    • 수퍼클래스의 내부 코드가 실행되기 이전에 빈 객체를 생성하여 인스턴스는 this에 바인딩된다.
    • 서브클래스가 생성한 인스턴스이기 때문에, new 연산자와 함께 호출된 클래스는 서브클래스 이기 때문에 new.target이 가리키는 서브클래스가 생성한 것으로 처리된다.
  3. 수퍼클래스의 인스턴스 초기화
    • new 연산자를 호출할 때 받은 인수를 constructor가 초기값으로 인스턴스의 프로퍼티를 초기화한다.
  4. 서브클래스 constructor로의 복귀와 this 바인딩
    • super의 호출이 끝나면 서브클래스의 constructor로 돌아온다.
    • super가 반환한 인스턴스에 this가 바인딩된다.
    • super가 인스턴스를 반환하기 때문에 super 이전에 this를 참조할 수 없는 이유가 이 때문이다.
  5. 서브클래스의 인스턴스 초기화
    • 서브클래스가 가지고 있는 constructor 내부의 프로퍼티를 초기화한다.
  6. 인스턴스 반환
    • 클래스의 처리가 끝나면 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.

 

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

  • 표준 빌트인 객체도 [[Construct]] 내부 메서드를 갖는 생성자 함수이기 때문에 extends 를 이용해 확장할 수 있다.
728x90
반응형