본문 바로가기

Javascript

[모던 자바스크립트 Deep Dive] 22. 클래스

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

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

  • 클래스를 new 연산자 없이 호출하면 에러 발생 /생성자 함수를 new 연산자 없이 호출하면 일반 함수로서 호출
  • 클래스는 상속을 지원하는 extends 와 super 키워드를 제공 /생성자 함수는 extends 와 super 키워드를 지원하지 X
  • 클래스는 호이스팅이 발생하지 않는 것처럼 동작 /함수 선언문으로 정의된 생성자 함수는 함수 호이스팅이, 함수 표현식으로 정의한 생성자 함수는 변수 호이스팅이 발생
  • 클래스 내의 모든 코드에는 암묵적으로 strict mode가 지정되어 실행되며 strict mode 해제 X /생성자 함수는 임묵적으로 strict mode 지정 X
  • 클래스의 constructor, 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 [[Enumerable]] 의 값이 false 열거 X

클래스는 새로운 객체 생성의 매커니즘

클래스 정의

// 클래스 선언문
class Person{} // 파스칼 케이스 사용

// 익명 클래스 표현식 
const Person = class{};

// 기명 클래스 표현식
const Person = class MyClass{};

 

클래스는 일급 객체로 다음과 같은 특징을 가짐

  • 무명의 리터럴로 생성 가능 (런타임 생성 가능)
  • 변수나 자료구조 (객체, 배열 등)에 저장 가능
  • 함수의 매개변수에게 전달 가능
  • 함수의 반환값으로 사용 가능
  • 값처럼 사용 가능

클래스 몸체에서 정의 할 수 있는 메서드는 constructor (생성자), 프로토타입 메서드, 정적 메서드 세 가지가 있음

// 클래스 선언문
class Person{
 // 생성자 
 constructor(name){
 // 인스턴스 생성 및 초기화
 this.name = name; // name 프로퍼티는 public
 }
 
 // 프로토타입 메서드
 sayHi(){
  console.log(`프로토타입 메서드! 저는 ${this.name}입니다`);
 }
 
 // 정적 메서드
 static sayHello(){
  console.log("정적 메서드!")
 }
}

// 인스턴스 생성
const me = new Person("Echichi");

// 인스턴스의 프로퍼티 참조
console.log(me.name); // Echichi
// 프로토타입 메서드 호출
me.sayHi(); // 프로토타입 메서드! 저는 Echichi입니다
// 정적 메서드 호출
Person.sayHello(); // 정적 메서드!

 

클래스 호이스팅

클래스 선언문으로 정의한 클래스는 함수 선언문과 같이 런타임 이전 (소스코드 평가 과정)에 먼저 평가되어 함수 객체 생성

→ 클래스는 constructor 생성자 함수로서 호출할 수 있는 함수로 파악되어 함수 객체를 생성하는 시점에 프로토타입도 덩달아 생성

→ 프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재하기 때문

const Person = '';

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

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

클래스 선언문 이전에 일시적 사각지대에 빠지기 떄문에 호이스팅이 발생하지 않는 것처럼 동작

var, let, const, function, class 키워드를 사용하여 선언된 모든 식별자는 런타임 이전에 먼저 실행되기 때문에 호이스팅 됨

 

인스턴스 생성

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

class 클래스명 {}

// 인스턴스 생성
const 식별자 = new 클래스명();

클래스는 인스턴스를 생성하는 것이 유일한 존재 이유이므로 반드시 new 연산자와 함께 호출해야함

const Person = class MyClass{};
const me = new Person();

const you = mew MyClass(); // ReferenceError : MyClass is not defined

기명 함수 표현식과 마찬가지로 클래스 표현식에서 사용한 클래스 이름은 외부 코드에서 접근 불가능

 

메서드

클래스 몸체에서 정의할 수 있는 메서드는 constructor (생성자), 프로토타입 메서드, 정적 메서드 세 가지가 있음

 

1. constructor 

  • 인스턴스를 생성하고 초기화하기 위한 특수한 메서드
  • 이름 변경 불가 
  • 특성이 생성자 함수와 같음 (프로토타입과의 연결, 스코프 체인 구성, this)
  • 모든 함수 객체가 가지고 있는 prototype 프로퍼티가 가리키는 프로토타입 객체의 constructor 프로퍼티는 클래스 자신을 가리킴 (생성자 함수 또는 리터럴 표기법으로 객체를 생성하면 프로토타입은 생성된 객체의 [[Prototype]] 내부 슬롯에 할당)
  • 클래스 몸체에 정의한 constructor는 메서드로 해석되는 것이 아니라 클래스가 평가되어 constructor의 기술된 동작을 하는 함수 객체가 생성
  • 클래스의 constructor 메서드와 프로토타입의 constructor은 다름 (프로토타입 constructor : 모든 프로토타입이 가지고 있는 프로퍼티이며 생성자 함수를 가리킴)
  • constructor는 클래스 내에 최대 한 개만 존재 가능
  • constructor는 생략 가능 = 빈 constructor 가 암묵적으로 정의 (constructor(){})

2. 프로토타입 메서드

생성자 함수를 사용하여 인스턴스를 생성하는 경우 프로토타입 메서드를 생성하기 위해서는 명시적으로 프로토타입에 메서드 추가해야함

class Person{
 // 생성자
 constructor(name){
  // 인스턴스 생성 및 초기화
  this.name = name;
 }
 
 // 프로토타입 메서드 
 sayHi(){
  console.log(`프로토타입 메서드! 저는 ${this.name}입니다`);
 }
}

const me = new Person("Echichi");
me.sayHi(); // 프로토타입 메서드! 저는 Echichi입니다

클래스 몸체에서 정의한 메서드는 prototype 프로퍼티에 추가하지 않아도 기본적으로 프로토타입 메서드가 됨

// me 객체의 프로토타입은 Person.prototype
Object.getPrototype(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.contructor === Person; // true

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

 

3. 정적 메서드

정적 메서드 : 인스턴스를 생성하지 않아도 호출될 수 있는 메서드 

class Person {
 // 생성자
 constructor(name){
  // 인스턴스 생성 및 초기화
  this.name = name;
 }
 
 // 정적 메서드
 static sayHi(){
  console.log("Hi!");
 }
}
  • 정적 메서드는 클래스에 바인딩된 메서드가 됨
  • 클래스는 함수 객체로 평가되므로 자신의 프로퍼티/ 메서드 소유 가능
  • 클래스는 클래스 정의 (클래스 선언문이나 클래스 표현식)가 평가되는 시점에서 함수 객체가 되므로 인스턴스와 달리 별다른 생성 과정 필요 X
  • 클래스의 정적 메서드는 정의 이후 인스턴스를 생성하지 않아도 호출 가능

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

  • 정적 메서드와 프로토타입 메서드는 자신이 속해 있는 프로토타입 체인이 다르다
  • 정적 메서드는 클래스로 호출하고 프로토타입 메서드는 인스턴스로 호출한다
  • 정적 메서드는 인스턴스 프로퍼티를 참조할 수 없지만 프로토타입 메서드는 인스턴스 프로퍼티를 참조할 수 있다
  • 정적 메서드는 클래스를 호출해야 하므로 정적 메서드 내부의 this 는 인스턴스가 아닌 클래스를 가리킨다
  • 표준 빌트인 객체인 Math, Number, JSON, Object, Reflect 등은 다양한 정적 메서드를 가지고 있으며 이 메서드들은 애플리케이션 전역에서 사용할 유틸리티 함수다
// 표준 빌트인 객체의 정적 메서드
Math.max(1, 3); 	// 3
Number.isNaN(NaN); 	// true
JSON.stringify({ a : 1 }) // "{ 'a' : 1 }"

 

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

  • function 키워드를 생략한 메서드 축약 표현을 사용
  • 객체 리터럴과는 다르게 클래스에 메서드를 정의할 때는 콤마가 필요 X
  • 암묵적으로 strict mode로 실행
  • 프로퍼티 어트리뷰트 [[Enumerable]] 값이 false 
  • 내부 메서드 [[Construct]] 를 갖지 않는 non-contruct → new 연산자와 함께 호출 불가

 

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

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

  1. new 연산자와 함께 클래스를 호출하면 constructor의 내부 코드가 실행되기에 앞서 암묵적으로 빈 객체 생성 ( 이 빈 객체가 완성되진 않은 인스턴스)
  2. 클래스가 생성한 인스턴스의 프로토타입으로 클래스의 prototype 프로퍼티가 가리키는 객체가 설정 됨
  3. 생성된 빈 객체 즉 인스턴스는 this 에 바인딩
  4. constructor 내부의 this 는 클래스가 생성한 인스턴스를 가리킴

2. 인스턴스 초기화

  1. constructor의 내부 코드가 실행되어 this에 바인딩되어 있는 인스턴스에 프로퍼티 추가
  2. constructor 가 인수로 전달받은 초기값으로 인스턴스의 프로퍼티 값 초기화

3. 인스턴스 반환

  1. 클래스의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this 가 암묵적으로 반환
class Person {
 // 생성자
 constructor(name){
  // 1. 암묵적으로 인스턴스가 생성(빈 객체)되고 this에 바인딩 됨
  console.log(this); // Person{}
  // 클래스가 생성한 인스턴스의 프로토타입 === 클래스의 prototype 프로퍼티 객체
  console.log(Object.getPrototypeOf(this) === Person.prototype); // true
  
  // 2. this에 바인딩되어 있는 인스턴스 초기화
  this.name = name;
  
  // 3. 완성된 인스턴스가 바인딩된 this 가 암묵적으로 반환
 }
}

 

프로퍼티

1. 인스턴스 프로퍼티

인스턴스 프로퍼티는 constructor 내부에서 정의해야 함

constructor 내부에서 this 에 인스턴스 프로퍼티 추가 (= 생성자 함수가 생성할 인스턴스의 프로퍼티를 정의하는 것과 비슷) → 인스턴스에 프로퍼티가 추가되어 인스턴스가 초기화 됨

 

2. 접근자 프로퍼티

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

class Person {
 constructor (firstName, lastName){
  this.firstName = firstName;
  this.lastName = lastName;
 }
 
 // fullName은 접근자 함수로 구성된 접근자 프로퍼티
 // getter 함수
 get fullName(){
  return `${this.lastName} ${this.firstName}`;
 }
 
 // setter 함수
 set fullName(name){
  [this.lastName, this.firstName] = name.split(' ');
 }
}

const me = new Person('chichi', 'E');

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

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

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

// fullName 은 접근자 프로퍼티다
// 접근자 프로퍼티는 get, set, enumerable, configurable 프로퍼티 어트리뷰트를 가짐
console.log(Object.getOwnPropertyDescriptor(Person.property, 'fullName'));
// {get : f, set: f, enumerable : false, configurable : true}
getter  setter 
호출하는 것이 아니라 프로퍼티처럼 참조하는 형식으로 사용 호출하는 것이 아니라 프로퍼티처럼 값을 할당하는 형식으로 사용
반드시 무언가를 반환 반드시 매개변수 필요 (단 하나의 값)

클래스의 메서드 = 프로토타입 메서드 → 클래스의 접근자 프로퍼티 = 프로토타입 프로퍼티

 

3. 클래스 필드 정의 제안

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

최신 브라우저 또는 최신 Node.js(버전 12 이상) 에서는 클래스 필드를 클래스 몸체에 정의 가능

class Person{
 // 클래스 필드 정의
 name = "Echichi"; 
}

const me = new Person();
console.log(me); // Person {name : "Echichi"}
  • 클래스 필드를 참조하는 경우 this 를 반드시 사용해야함
  • 클래스 필드에 초기값을 할당하지 않으면 undefined 
  • 인스턴스를 생성할 때 외부의 초기값으로 클래스 필드를 초기화해야 한다면 constructor 에서 클래스 필드를 초기화 해야함
  • 클래스 필드를 통해 메서드 정의 가능 → 프로토타입 메서드가 아닌 인스턴스 메서드가 됨 ( 클래스 필드는 인스턴스 프로퍼티이기 때문 )

4. private 필드 정의 제안

최신 브라우저 또는 최신 Node.js(버전 12 이상) 에서는 private 필드 정의 가능

private 필드의 선두에 #을 붙이고 private 필드를 참조할 때도 #을 붙임

class Person{
 // private 필드 정의
 #name = '';
 
 constructor(name){
  // private 필드 참조
  this.#name = name;
 }
 
 // 접근자 프로퍼티 name
 get sayName(){
  // private 필드를 참조하여 trim한 다음 반환
  return this.#name.trim();
 }
}

const me = new Person("Echichi");

// private 필드 #name 은 클래스 외부에서 참조불가 
console.log(me.#name);
// SyntaxError : Private field must be declared in an enclosing class
console.log(me.sayName); // Echichi

접근자 프로퍼티를 통해 간접적으로 접근하는 방법은 유효

private 필드는 반드시 클래스 몸체에 정의해야하며 constructor에 정의하면 에러

접근 가능성 public private
클래스 내부 O O
자식 클래스 내부 O X
클래스 인스턴스를 통한 접근 O X

 

5. static 필드 정의 제안

최신 브라우저 또는 최신 Node.js(버전 12 이상) 에서는 static public/ private 필드 정의 가능

class MyMath{
 // static public 필드 정의
 static PI = 22 / 7;
 
 // static private 필드 정의
 static #num = 10;
 
 // static 메서드 
 static increment(){
  return ++My.Math.#num;
 }
}

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

상속에 의한 클래스 확장

1. 클래스 상속과 생성자 함수 상속

상속에 의한 클래스 확장은 기존 클래스를 상속받아 새로운 클래스를 확장 정의 하는 것

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 , weigth : 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

 

2. extends 키워드

상속을 통해 클래스를 확장하려면 extends 키워드를 사용하여 상속받을 클래스를 정의

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

// 서브(파생/자식) 클래스
class Derived extends Base{}

서브클래스 : 상속을 통해 확장된 클래스 (파생 클래스, 자식 클래스)

수퍼클래스 : 서브클래스에게 상속된 클래스 (베이스 클래스, 부모 클래스)

 

3.동적 상속

  • extends 키워드는 클래스뿐만 아니라 [[Construct]] 내부 메서드를 갖는 함수 객체로 평가될 수 있는 모든 표현식 (생성자 함수)을 상속받아 클래스 확장 가능
  • extends 앞에는 반드시 클래스가 와야함
  • 동적으로 상속받을 대상 결정 가능
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

 

4. 서브클래스의 constructor

슈퍼클래스와 서브클래스 모두 constructor를 생략하면 빈 객체가 생성됨 → 프로퍼티를 소유하는 인스턴스를 생성하려면 constructor 내부에서 인스턴스에 프로퍼티를 추가해야함

// 수퍼클래스
class Base {
 constructor(){}
}

// 서브 클래스
class Derived extends Base{}

/*
위의 수퍼클래스에는 다음과 같이 암묵적으로 constructor가 정의됨
class Base {
 constructor(){}
}
*/

/*
위의 서브클래스에는 다음과 같이 암묵적으로 constructor가 정의됨
class Derived extends Base{
 constructor(...args){ super(...args); }
}
*/

 

5. super 키워드

super 키워드 : 함수처럼 호출할 수도 있고 this와 같이 식별자처럼 참조할 수 있는 특수한 키워드

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

- super 호출

super를 호출하면 수퍼클래스의 constructor(super-constructor)를 호출한다

  • 수퍼클래스의 constructor 내부에서 추가한 프로퍼티를 그대로 갖는 인스턴스를 생성한다면 서브클래스의 constructor 생성 가능
  • new 연산자와 함께 서브클래스를 호출하면서 전달한 인수는 모두 서브클래스에서 암묵적으로 정의된 cosntructor의 super 호출을 통해 수퍼클래스의 constructor 에 전달됨
// 수퍼 클래스
class Base{
 // ④ Base 클래스의 constructor에 일부가 전달
 constructor(a,b){ 
  this.a = a;
  this.b = b;
 }
}

// 서브 클래스
class Derived extends Base {
 // constructor 작성 생략시 다음과 같이 암묵적으로 constructor가 정의됨
 // constructor(...args) { super(...args);}
 constrcutor(a, b, c){ // ② Derived 클래스에 인수 전달
 // ③ super 호출
  super(a, b);
  this.c = c;
 }
}
// 수퍼클래스의 constructor 에 전달할 필요가 있는 인수는 서브클래스의 constructor에서 호출하는 super을 통해 전달
// // ① Derived 클래스 호출
const derived = new Derived(1, 2); 
console.log(derived); // Derived {a: 1, b: 2}

 

super 호출 시 주의사항

  • 서브클래스에서 constructor를 생략하지 않는 경우 서브 클래스의 constructor에서는 반드시 super 을 호출해야한다
  • 서브클래스의 constructor 에서 super를 호출하기 전에는 this 를 참조할 수 없다
  • super는 반드시 서브클래스의 constructor에서만 호출한다. 서브클래스가 아닌 클래스의 constructor나 함수에서 super를 호출하면 에러가 발생한다

- super 참조

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

서브클래스의 프로토타입 메서드 내에서 super.메서드는 수퍼클래스의 프로토타입 메서드를 가리킨다

  • super 참조를 통해 수퍼클래스의 메서드를 참조하려면 super가 수퍼클래스의 메서드가 바인딩된 객체 , 즉 수퍼클래스의 prototype 프로퍼티에 바인딩된 프로토타입을 참조할수 있어야 한다
// 수퍼클래스
class Base{
 constructor(name){
  this.name = name;
 }
 sayHi(){
  return `수퍼클래스! ${this.name}`;
 }
}

class Derived extends Base{
 sayHi(){
  // __super 는 Base.prototype 을 가리킨다
  const __super = Object.getPrototypeOf(Derived.prototype);
  return `${__super.sayHi.call(this)} 난 서브클래스!`;
 }
}

super 참조가 동작하기 위해서는 super를 참조하고 있는 메서드(Derived.sayHi)가 바인딩되어 있는 객체(Derived.prototype)의 프로토타입(Base.prototype)을 찾을 수 있어야 함

이를 위해 메서드는 내부 슬롯 [[HomeObject]] 를 가지며 자신을 바인딩하고 있는 객체를 가리킴

super 참조의사를 코드로 표현

/*
[[HomeObject]]는 메서드 자신을 바인딩하고 있는 객체를 가리킨다
[[HomeObject]]를 통해 매서드 자신을 바인딩하고 있는 객체의 프로토타입을 찾을 수 있다
예를 들어 Derived 클래스의 sayHi 메서드는 Derived.prototype에 바인딩 되어 있다
따라서 Derived 클래스의 sayHi 메서드의 [[HomeObject]]는 Derived.prototype이고
이를 통해 Derived 클래스의 sayHi 메서드 내부의 super 참조가 Base.prototype 으로 결정된다
따라서 super.sayHi는 Base.prototype.sayHi를 가리키게 된다
*/
super = Object.getPrototypeOf([[HomeObject]])

const obj = {
 // foo는 ES6의 메서드 축약 표현으로 정의한 메서드다. 따라서 [[HomeObject]]를 갖는다
 foo(){}.
 // bar는 ES6의 메서드 축약 표현으로 정의한 메서드가 아니라 일반 함수로 [[HomeObject]]를 갖지 않는다
 bar : function(){}
};
  • [[HomeObject]] 를 가지는 함수만이 super 를 참조 가능 → ES6의 메서드 축약표현으로 정의된 함수만이 super 참조 가능

서브클래스의 정적 메서드 내에서 super.sayHi는 수퍼클래스의 정적 메서드 sayHi를 가리킨다

// 수퍼클래스
class Base{
 static sayHi(){
  return "Hi";
 }
}
// 서브클래스
class Derived extends Base{
 static sayHi(){
  // super.sayHi 는 수퍼클래스의 정적 메서드를 가리킨다
  return `${super.sayHi()}! Echichi`;
 }
}

 

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

1. 서브클래스의 super 호출

  • 자바스크립트 엔진은 클래스를 평가할 때 다른 클래스를 상속받지 않는 클래스 (그리고 생성자 함수)는 내부슬롯 [[ConstructorKind]] 값으로 base를 설정하고 다른 클래스를 상속받는 서브클래스는 derived로 설정
  • 다른 클래스를 상속받지 않는 클래스(그리고 생성자 함수) 는 new 연산자와 함께 호출되었을 때 암묵적으로 빈 객체 즉 인스턴스를 생성하고 이를 this 에 바인딩
  • 서브클래스는 자신이 직접 인스턴스를 생성하지 않고 수퍼클래스에게 인스턴스 생성을 위임 → 서브클래스의 constructor에서 반드시 super 을 호출해야하는 이유

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

  • new 연산자와 함께 호출된 클래스는 서브클래스로 생성된 인스턴스는 new.target이 가리키는 서브클래스가 생성한 것으로 처리

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

  • this에 바인딩 되어 있는 인스턴스에 프로퍼티를 추가하고 constructor가 인수로 전달받은 초기값으로 인스턴스의 프로퍼티 초기화

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

  • super 호출이 종료되고 제어 흐름이 서브클래스 constructor로 복귀
  • super가 반환한 인스턴스가 this에 바인딩
  • 서브클래스는 별도의 인스턴스를 생성하지 않고 super 가 반환한 인스턴스를 this에 바인딩하여 그대로 사용 → super 가 호출되지 않으면 인스턴스가 생성되지 않고 this 바인딩도 못하기 때문에 this 참조 불가

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

  • this에 바인딩되어 있는 인스턴스에 프로퍼티를 추가하고 constructor가 인수로 전달받은 초기값으로 인스턴스의 프로퍼티 초기화

6. 인스턴스 반환

  • 클래스의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this 가 암묵적으로 반환

 

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

// Array 생성자 함수를 상속받아 확장한 MyArray
class MyArray extends Array{
 // 중복된 배열 요소를 제거하고 반환한다
 // MyArray클래스의 인스턴스 반환
 uniq(){
  return this.filter((v, i, self)=>(self.indexOf(v) === i));
 }
 
 // 모든 배열 요소의 평균을 구한다
 // MyArray클래스의 인스턴스 반환
 avg(){
  return this.reduce((pre, cur)=>(pre + cur, 0))/ this.length;
 }
}

const myArray = new Array(1, 1, 2, 3);

console.log(myArray.uniq()); // MyArray(3) [1, 2, 3]
console.log(myArray.avg()); // 1.75

// MyArray 클래스 인스턴스를 반환하기 때문에 메서드 체이닝 가능
console.log(myArray.filter((v)=>(v % 2)).uniq().avg())