[JS] 25장 클래스

JH Cho·2023년 1월 17일
0

모던JS DeepDive 공부

목록 보기
27/27

Deepdive 공부 정리용 글입니다.

25.1_ 클래스란

  • JS는 프로토타입 기반 객체지향 언어

  • 프로토타입 기반 객체지향 언어는 클래스가 필요 없는 객체지향 언어!

  • ES5의 함수와 프로토타입을 통한 객체지향 언어의 상속 구현

// ES5 생성자 함수
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
console.log(me);
// Person {name: 'Lee'}
  • ES6에서 도입된 class는 클래스 기반 객체지향 프로그래밍 언어와 매우 비슷한 새로운 객체 생성 메커니즘 제시.
  • 하지만 이는 새로운 객체지향 모델이 아닌 기존 프로토타입 기반 패턴을 클래스 기반 패턴처럼 사용 가능하도록 하는 문법적 설탕임이라고 본다.. 하지만?(곧 설명)
  • 클래스와 생성자 함수는 비슷하지만 클래스가 더 엄격하며 생성자 함수가 제공하지 않는 기능 또한 제공한다.

    클래스와 생성자함수의 차이점

  1. new 연산자 없이 호출 -> class(에러) & 생성자 함수(일반함수로 호출)
  2. 클래스는 extends와 super 키워드 제공
  3. 호이스팅 -> class(발생하지 않는 것처럼) & 생성자함수(호이스팅), 함수표현식(변수 호이스팅)
  4. strict mode -> class(해제불가 지정) & 생성자함수(암묵적 지정X)
  5. 클래스의 constructor, 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 [[Enumerable]] 값이 false로 열거되지 않는다.
  • 생성자 함수와 클래스는 프로토타입 기반의 객체지향을 구현하는 것은 비슷하지만 클래스가 생성자 함수보다 견고하고 명료하다.
  • 클래스의 extends와 super 키워드는 상속 관계 구현을 더욱 간결하고 명료하게 함.
  • 따라서 클래스를 문법적 설탕보다는 새로운 객체 생성 메커니즘으로 보는 것이 합당함.

25.2_ 클래스 정의

  • 클래스는 class 키워드를 사용하여 정의.
  • 클래스 이름은 생성자함수와 마찬가지로 Pascal case
    class Person {}
  • 표현식도 가능
const Person = class{};
const Person = class Myclass{};
  • 즉 클래스는 표현식으로 정의 가능하므로 일급 객체라는 의미.

    • 🔎 일급 객체의 특징
      무명의 리터럴로 생성 가능(런타임 생성)
      변수나 자료구조(객체, 배열)에 저장 가능
      함수의 매개변수에 전달 가능
      함수의 반환값으로 사용 가능
  • 클래스는 컨스트럭터, 프로토타입 메서드, 정적 메서드 정의 가능.

  • 생성자 함수와 클래스의 비교

//생성자 함수
var Person = (function(){
  function Person(name){
    this.name = name;
  }
  
  // 프로토타입 메서드
  Person.prototype.sayHi = function(){
    console.log('Hi, My name is' + this.name);
  }
  
  // 정적 메서드
  Person.sayHello = function () {
    console.log('Hello!');
  }
  
  //생성자 함수 반환
  return Person;
}());
------------------
// 클래스 선언문
class Person {
  // 생성자
  constructor(name) {
    // 인스턴스 생성 및 초기화
    this.name = name; // name 프로퍼티는 public하다.
  }

  // 프로토타입 메서드
  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!

정적 메서드?

  • 정적 메서드는 prototype이 아닌 클래스 함수 자체에 메서드를 설정하는 것
  • 정적 메서드는 특정 클래스 인스턴스가 아닌 클래스 '전체'에 필요한 기능을 만들 때 사용 가능
class Article {
  constructor(title, date) {
    this.title = title;
    this.date = date;
  }

  static createTodays() {
    // this는 Article입니다.
    return new this("Today's digest", new Date());
  }
}

let article = Article.createTodays();

alert( article.title ); // Today's digest

25.3_ 클래스 호이스팅

  • 클래스는 평가과정에서 평가되어 함수 객체를 생성
  • 주의! 호이스팅은 발생하지만 런타임 정의 이전에 참조는 불가하다
  • let const와 비슷
const Person = '';

{
  // 호이스팅이 발생하지 않는다면 ''이 출력되어야 한다.
  console.log(Person);
  // ReferenceError: Cannot access 'Person' before initialization

  // 클래스 선언문
  class Person {}
}

25.4_ 인스턴스 생성

  • 클래스는 생성자 함수로 new 연산자와 함께 호출되어 인스턴스를 생성
class Person {}

const me = new Person{}

25.5_ 메서드

  • 클래스 몸체에서 정의 가능한 메서드
    • constructor(생성자)
    • 프로토타입 메서드
    • 정적 메서드

    25.5.1_ constructor

  • constructor는 인스턴스를 생성하고 초기화하기 위한 특수 메서드
class Person {
  // 생성자
  constructor(name) {
    // 인스턴스 생성 및 초기화
    this.name = name;
  }
}

// 클래스는 함수다.
console.log(typeof Person); // function
console.dir(Person);


// 인스턴스 생성
const me = new Person('Lee');
console.log(me);//Person {name: 'Lee'}
  • 생성자 함수와 마찬가지로 constructor 내부에서 this에 추가한 프로퍼티는 인스턴스 프로퍼티가 된다.
// 클래스
class Person {
  // 생성자
  constructor(name) {
    // 인스턴스 생성 및 초기화
    this.name = name;
  }
}

// 생성자 함수
function Person(name) {
  // 인스턴스 생성 및 초기화
  this.name = name;
}

프로토타입의 constructor와 class의 constructor차이

관련 없음.
프로토타입의 컨스트럭터는 모든 프로토타입이 가지고 있는 프로퍼티로 생성자 함수를 가리킴.

  • 인스턴스 생성 시 클래스 외부에서 인스턴스 프로퍼티의 초기값 전달하기
class Person {
  constructor(name, address) {
    // 인수로 인스턴스 초기화
    this.name = name;
    this.address = address;
  }
}

// 인수로 초기값을 전달한다. 초기값은 constructor에 전달된다.
const me = new Person('Lee', 'Seoul');
console.log(me); // Person {name: "Lee", address: "Seoul"}

25.5.2_ 프로토타입 메서드

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

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

// 프로토타입 지정 안해도 기본적으로 프로토타입 메서드가 됨.
sayBye = function(){
  console.log(`Bye, ${this.name}`);

const me = new Person('Lee');
me.sayHi(); // Hi! My name is Lee
  • 생성자 함수와 마찬가지로 클래스가 생성한 인스턴스는 프로토타입 체인의 일원이 됨.
// me 객체의 프로토타입은 Person.prototype이다.
Object.getPrototypeOf(me) === Person.prototype; // -> true
me instanceof Person; // -> true

// Person.prototype의 프로토타입은 Object.prototype이다.
Object.getPrototypeOf(Person.prototype) === Object.prototype; // -> true
me instanceof Object; // -> true

// me 객체의 constructor는 Person 클래스다.
me.constructor === Person; // -> true

25.5.3_ 정적 메서드

  • 정적 메서드는 인스턴스를 생성하지 않아도 호출 가능한 메서드

  • 생성자 함수의 정적 메서드 생성 방법

    • 명시적으로 생성자 함수에 메서드 추가
// 생성자 함수
function Person(name){
  this.name = name;
}

// 정적 메서드
Person.sayHi = function () {
  console.log('Hi!');
};

Person.sayHi(); //Hi!
  • 클래스의 정적 메서드 생성 방법
class Person {
  //생성자
  constructor(name) {
    //인스턴스 생성 및 초기화
    this.name = name;
  }
  
  static sayHi() {
    console.log('Hi');
  }
}
  • 정적 메서드는 프로토타입 메서드처럼 인스턴스로 호출하지 않고 클래스로 호출
// 정적 메서드는 클래스로 호출한다.
// 정적 메서드는 인스턴스 없이도 호출할 수 있다.
Person.sayHi(); // Hi!
  • 정적 메서드는 인스턴스로 호출 불가.
  • 정적 메서드가 바인딩된 클래스는 인스턴스의 프로토타입 체인 상에 존재하지 않음.
  • 즉, 상속되지 않음
// 인스턴스 생성
const me = new Person('Lee');
me.sayHi(); // TypeError: me.sayHi is not a function

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

  1. 정적 메서드와 프로토타입 메서드는 속해있는 프로토타입 체인이 다름
  2. 정적 메서드는 클래스로 호출, 프로토타입 메서드는 인스턴스로 호출
  3. 정적 메서드는 인스턴스 프로퍼티를 참조 불가 / 프로토타입 메서드는 인스턴스 프로퍼티를 참조 가능
  • 메서드 내부의 this는 메서드를 소유한 객체가 아닌 메서드를 호출한 객체에 바인딩됨( 메서드 이름 앞의 마침표 '.' 연산자 앞에 기술한 객체에 바인딩)
  • 프로토타입 메서드는 위와 같이 바인딩되지만 정적 메서드는 class에 바인딩 되는 것이 다름.
  • 사용 예)
// 표준 빌트인 객체의 정적 메서드
Math.max(1, 2, 3);          // -> 3
Number.isNaN(NaN);          // -> true
JSON.stringify({ a: 1 });   // -> "{"a":1}"
Object.is({}, {});          // -> false
Reflect.has({ a: 1 }, 'a'); // -> true

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

클래스에서 정의한 메서드의 특징
1. function 키워드를 생략한 메서드 축약 표현 사용
2. 객체 리터럴과 다르게 클래스에 메서드 정의 시 콤마 불필요
3. 암묵적으로 strict mode로 실행
4. for...in 문이나 Objcet.keys 메서드 등으로 열거 불가
5. 내부 메서드 [[Construct]]를 갖지 않는 non-constructor.
(즉, new 연산자와 함께 호출 불가)

25.6_ 클래스이 인스턴스 생성 과정(435p)

  1. 인스턴스 생성과 this 바인딩
  2. 인스턴스 초기화
  3. 인스턴스 반환

25.7_ 프로퍼티

25.7.1_ 인스턴스 프로퍼티

  • 인스턴스 프로퍼티는 constructor 내부에서 정의
class Person {
  constructor(name) {
    // 인스턴스 프로퍼티
    this.name = name;
  }
}

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

constructor 내부 코드 실행되기 이전에 constructor 내부 this에는 이미 클래스가 암묵적으로 생성한 인스턴스인 빈 객체가 바인딩 됨
-> 이로써 클래스가 암묵적 생성한 빈 객체(인스턴스)에 프로퍼티가 추가되어 인스턴스 초기화

25.7.2_ 접근자 프로퍼티

접근자 프로퍼티는 자체적으로 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수로 구성된 프로퍼티

  • 생성자함수로
const person = {
  // 데이터 프로퍼티
  firstName: 'Ungmo',
  lastName: 'Lee',

  // fullName은 접근자 함수로 구성된 접근자 프로퍼티다.
  // getter 함수
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  },
  // setter 함수
  set fullName(name) {
    // 배열 디스트럭처링 할당: "36.1. 배열 디스트럭처링 할당" 참고
    [this.firstName, this.lastName] = name.split(' ');
  }
};

// 데이터 프로퍼티를 통한 프로퍼티 값의 참조.
console.log(`${person.firstName} ${person.lastName}`); // Ungmo Lee

// 접근자 프로퍼티를 통한 프로퍼티 값의 저장
// 접근자 프로퍼티 fullName에 값을 저장하면 setter 함수가 호출된다.
person.fullName = 'Heegun Lee';
console.log(person); // {firstName: "Heegun", lastName: "Lee"}

// 접근자 프로퍼티를 통한 프로퍼티 값의 참조
// 접근자 프로퍼티 fullName에 접근하면 getter 함수가 호출된다.
console.log(person.fullName); // Heegun Lee

// fullName은 접근자 프로퍼티다.
// 접근자 프로퍼티는 get, set, enumerable, configurable 프로퍼티 어트리뷰트를 갖는다.
console.log(Object.getOwnPropertyDescriptor(person, 'fullName'));
// {get: ƒ, set: ƒ, enumerable: true, configurable: true}
  • 클래스의 경우
class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  // fullName은 접근자 함수로 구성된 접근자 프로퍼티다.
  // getter 함수
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  // setter 함수
  set fullName(name) {
    [this.firstName, this.lastName] = name.split(' ');
  }
}

const me = new Person('Ungmo', 'Lee');

// 데이터 프로퍼티를 통한 프로퍼티 값의 참조.
console.log(`${me.firstName} ${me.lastName}`); // Ungmo Lee

// 접근자 프로퍼티를 통한 프로퍼티 값의 저장
// 접근자 프로퍼티 fullName에 값을 저장하면 setter 함수가 호출된다.
me.fullName = 'Heegun Lee';
console.log(me); // {firstName: "Heegun", lastName: "Lee"}

// 접근자 프로퍼티를 통한 프로퍼티 값의 참조
// 접근자 프로퍼티 fullName에 접근하면 getter 함수가 호출된다.
console.log(me.fullName); // Heegun Lee

// fullName은 접근자 프로퍼티다.
// 접근자 프로퍼티는 get, set, enumerable, configurable 프로퍼티 어트리뷰트를 갖는다.
console.log(Object.getOwnPropertyDescriptor(Person.prototype, 'fullName'));
// {get: ƒ, set: ƒ, enumerable: false, configurable: true}

25.7.3_ 클래스 필드 정의 제안(439)

  • 클래스 필드란 클래스 기반 객체지향 언어에서 클래스가 생성할 인스턴스의 프로퍼티를 가리키는 용어.

  • JS도 클래스 기반처럼 작동하도록 바뀜

class Person{
  //클래스 필드 정의
  name = 'Lee';

// this에 클래스 필드 바인딩 X
  this.age = 12; (X)

//참조하는 경우에는 this 필요
	constructor() {
  		console.log(name); //refer error
	}

 gender;// 할당안하면 undefined
}

const me = new Person('Lee');
// 버전 12 이후 node.js에서는 정상 작동.
  • 인스턴스 초기화 필요 있을 시에는
class Person {
  //자동으로 추가되기 때문에 클래스 필드 정의할 필요 없다.
  constructor(name) {
    this.name = name;
  }
}

const me = new Person('Lee');
console.log(me); // Person {name: "Lee"}
  • 함수는 일급객체로 함수를 클래스 필드에 할당 가능
class Person {
  // 클래스 필드에 문자열을 할당
  name = 'Lee';

  // 클래스 필드에 함수를 할당
  getName = function () {
    return this.name;
  }
  // 화살표 함수로 정의할 수도 있다.
  // getName = () => this.name;
}

const me = new Person();
console.log(me); // Person {name: "Lee", getName: ƒ}
console.log(me.getName()); // Lee

위처럼 클래스 필드에 함수를 할당하면 이 함수는 프로토타입 메서드가 아닌 인스턴스 메서드가 됨.
(모든 클래스 필드는 인스턴스 프로퍼티가 되므로 클래스필드에 함수 할당은 권장 X)

25.7.4 private 필드 정의

  • 인스턴스 프로퍼티는 언제나 외부 참조 가능한 public

  • TC39 프로세스 stage3에서 제안 : private 필드 정의는 선두에 '#'

  • Typescript는 private, public, protected 모두 지원

class Person {
  // private 필드 정의
  #name = '';

  constructor(name) {
    // private 필드 참조
    this.#name = name;
  }
}

const me = new Person('Lee');

// private 필드 #name은 클래스 외부에서 참조할 수 없다.
console.log(me.#name);
// SyntaxError: Private field '#name' must be declared in an enclosing class
  • 접근 가능성

    접근 여부-> 클래스내부/자식클래스내부/클래스인스턴스통한접근
    public / O / O / O
    private / O / X / X

  • private 필드 정의하는 방법

class Person {
  // private 필드 정의
  // 반드시 클래스 몸체에 정의!
  #name = '';

  // 만약 몸체에 정의 없이 아래의 constructor로만 정의하면 에러가 발생할 것이다.
  constructor(name) {
    this.#name = name;
  }

  // name은 접근자 프로퍼티다.
  get name() {
    // private 필드를 참조하여 trim한 다음 반환한다.
    return this.#name.trim();
  }
}

const me = new Person(' Lee ');
console.log(me.name); // Lee

25.7.5_ static 필드 정의

class MyMath {
  // static public 필드 정의
  static PI = 22 / 7;

  // static private 필드 정의
  static #num = 10;

  // static 메서드
  static increment() {
    return ++MyMath.#num;
  }
}

console.log(MyMath.PI); // 3.142857142857143
console.log(MyMath.increment()); // 11

25.8_ 상속에 의한 클래스확장(extends)

기존 클래스를 상속받아 새로운 클래스를 확장(extends)

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

p449 animal 예시 참고!

class Animal {
  constructor(age, weight) {
    this.age = age;
    this.weight = weight;
  }

  eat() { return 'eat'; }

  move() { return 'move'; }
}

// 상속을 통해 Animal 클래스를 확장한 Bird 클래스
class Bird extends Animal {
  fly() { return 'fly'; }
}

const bird = new Bird(1, 5);

console.log(bird); // Bird {age: 1, weight: 5}
console.log(bird instanceof Bird); // true
console.log(bird instanceof Animal); // true

console.log(bird.eat());  // eat
console.log(bird.move()); // move
console.log(bird.fly());  // fly

25.8.2_ extends 키워드

class Base {}

class Derived extends Base{}

25.8.3_ 동적 상속

  • 생성자 함수도 상속 가능
// 생성자 함수
function Base(a) {
  this.a = a;
}

// 생성자 함수를 상속받는 서브클래스
class Derived extends Base {}

const derived = new Derived(1);
console.log(derived); // Derived {a: 1}
  • 동적 상속
function Base1() {}

class Base2 {}

let condition = true;

// 조건에 따라 동적으로 상속 대상을 결정하는 서브클래스
class Derived extends (condition ? Base1 : Base2) {}

const derived = new Derived();
console.log(derived); // Derived {}

console.log(derived instanceof Base1); // true
console.log(derived instanceof Base2); // false

25.8.4_ 서브클래스의 constructor

  • class에 constructor 생략 시 클래스에는 비어있는 constructor가 암묵적으로 정의됨
  • 자식 클래스에서 생략시 다음과 같은 constructor가 암묵적 생산
constructor(...args){super(...args);
 //super은 부모 클래스의 constructor를 호출하여 인스턴스 생산
  • 부모 자식 둘 다 생략 시
// 수퍼클래스
class Base {}

// 서브클래스
class Derived extends Base {}
----- constructor 암묵적 생산 --- 
// 수퍼클래스
class Base {
  constructor() {}
}

// 서브클래스
class Derived extends Base {
  constructor() { super(); }
}

const derived = new Derived();
console.log(derived); // Derived {}

25.8.5_ super 키워드

  • super는 함수처럼 호출도, this 처럼 참조할 수 있는 특수 키워드
    • super를 호출하면 부모클래스의 constructor 호출
    • super 참조하면 수퍼클래스의 메서드 호출 가능
  • super 호출
    <부모 클래스 constructor 호출>
// 수퍼클래스
class Base {
  constructor(a, b) { // ④
    this.a = a;
    this.b = b;
  }
}

// 서브클래스
class Derived extends Base {
  constructor(a, b, c) { // ② 인자 받음
    super(a, b); // ③ 부모의 cons 호출
    this.c = c; // 따로 c 생성
  }
}

const derived = new Derived(1, 2, 3); // ①
console.log(derived); // Derived {a: 1, b: 2, c: 3}

주의!
1. constructor 생략 안하는 경우 반드시 super호출하기.

class Base {}

class Derived extends Base {
  constructor() {
    // ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
    console.log('constructor call');
  }
}

const derived = new Derived();
  1. 자식클래스의 constructor에서 super 호출 전 this 참조 불가
class Base {}

class Derived extends Base {
  constructor() {
    // ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
    this.a = 1;
    super();
  }
}

const derived = new Derived(1);
  1. super는 반드시 자식 클래스의 constructor에서만 호출!
  • super 참조
    메서드 내에서 super를 참조하면 수퍼클래스의 메서드 호출 가능
  1. super.sayHi의 참조
// 수퍼클래스
class Base {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    return `Hi! ${this.name}`;
  }
}

// 서브클래스
class Derived extends Base {
  sayHi() {
    // super.sayHi는 수퍼클래스의 프로토타입 메서드를 가리킨다.
    return `${super.sayHi()}. how are you doing?`;
  }
}

const derived = new Derived('Lee');
console.log(derived.sayHi()); // Hi! Lee. how are you doing?
  1. 자식 클래스의 정적 메서드 내에서 suer.sayHi는 부모 클래스의 정적 메서드 sayHi 가리킴
// 수퍼클래스
class Base {
  static sayHi() {
    return 'Hi!';
  }
}

// 서브클래스
class Derived extends Base {
  static sayHi() {
    // super.sayHi는 수퍼클래스의 정적 메서드를 가리킨다.
    return `${super.sayHi()} how are you doing?`;
  }
}

console.log(Derived.sayHi()); // Hi! how are you doing?

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

// 수퍼클래스
class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }
>
  getArea() {
    return this.width * this.height;
  }
>
  toString() {
    return `width = ${this.width}, height = ${this.height}`;
  }
}
>
// 서브클래스
class ColorRectangle extends Rectangle {
  constructor(width, height, color) {
    super(width, height);
    this.color = color;
  }

  // 메서드 오버라이딩
  toString() {
    return super.toString() + `, color = ${this.color}`;
  }
}
>
const colorRectangle = new ColorRectangle(2, 4, 'red');
console.log(colorRectangle); // ColorRectangle {width: 2, height: 4, color: "red"}
>
// 상속을 통해 getArea 메서드를 호출
console.log(colorRectangle.getArea()); // 8
// 오버라이딩된 toString 메서드를 호출
console.log(colorRectangle.toString()); // width = 2, height = 4, color = red
  • 과정
    1. 자식 클래스의 super 호출
      부모는 내부슬롯 값이 "base" 자식은 "derived"로 설정됨.
      이 때문에 new 연산자 호출 시 둘은 구별된다.

      부모는 new 연산자 호출 시 암묵적으로 빈 객체를 생성하여 이를 this에 바인딩
      하지만 자식은 직접 인스턴스를 생성하지 않고 부모에게 인스턴스 생성을 위임하며 이것이 super를 호출해야하는 이유!
      따라서 자식이 super 호출 안하면 에러가 나는 이유는 인스턴스 생성의 주체가 부모클래스이기 때문이다.

      2. 부모클래스의 인스턴스 생성과 this 바인딩
      constructor 내부 코드가 실행되기 이전 빈 객체(인스턴스)가 생성되고 이 객체는 this에 바인딩 된다. 따라서 constructor 내부의 this는 인스턴스를 가리킨다.
    // 수퍼클래스
    class Rectangle {
      constructor(width, height) {
        // 암묵적으로 빈 객체, 즉 인스턴스가 생성되고 this에 바인딩된다.
        console.log(this); // ColorRectangle {}
        // new 연산자와 함께 호출된 함수, 즉 new.target은 ColorRectangle이다.
        console.log(new.target); // ColorRectangle
    ...

    이 때 new.target은 ColorRectangle을 가리키는 것을 통해 new 연산자를 통해 생성된 인스턴스는 new target이 가리키는 자식 클래스가 생성한 것으로 처리됨. 따라서 생성된 인스턴스의 프로토타입은 부모의 프로토타입(Rectagle.prototype)이 아닌 자식의 ColorRectangle.prototype 이다.

// 수퍼클래스
class Rectangle {
  constructor(width, height) {
    // 암묵적으로 빈 객체, 즉 인스턴스가 생성되고 this에 바인딩된다.
    console.log(this); // ColorRectangle {}
    // new 연산자와 함께 호출된 함수, 즉 new.target은 ColorRectangle이다.
    console.log(new.target); // ColorRectangle
>
    // 생성된 인스턴스의 프로토타입으로 ColorRectangle.prototype이 설정된다.
    console.log(Object.getPrototypeOf(this) === ColorRectangle.prototype); // true
    console.log(this instanceof ColorRectangle); // true
    console.log(this instanceof Rectangle); // true
...
  1. 수퍼클래스의 인스턴스 초기화
// 수퍼클래스
class Rectangle {
  constructor(width, height) {
    // 암묵적으로 빈 객체, 즉 인스턴스가 생성되고 this에 바인딩된다.
    console.log(this); // ColorRectangle {}
    // new 연산자와 함께 호출된 함수, 즉 new.target은 ColorRectangle이다.
    console.log(new.target); // ColorRectangle
// 생성된 인스턴스의 프로토타입으로 ColorRectangle.prototype이 설정된다.
console.log(Object.getPrototypeOf(this) === ColorRectangle.prototype); // true
console.log(this instanceof ColorRectangle); // true
console.log(this instanceof Rectangle); // true
// 인스턴스 초기화
this.width = width;
this.height = height;
console.log(this); // ColorRectangle {width: 2, height: 4}

}
...

~468 까지 책 읽어보는 게 낫겠다.
profile
주먹구구식은 버리고 Why & How를 고민하며 프로그래밍 하는 개발자가 되자!

0개의 댓글