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

HustleKang·2022년 4월 21일

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

프로토타입 기반의 객체 생성의 새로운 메커니즘
클래스는 생성자 함수보다 엄격하고 생성자 함수에서 제공하지 않는 기능도 제공

  • new 연산자 없이 클래스 호출시 에러
  • extends,super 키워드 제공
  • 호이스팅이 발생하지만 발생하지 않는 것처럼 동작 let,const처럼
  • 클래스 내의 코드는 암묵적으로 strict mode가 지정되어 실행
  • 클래스의 constructor, 프로토타입 메서드, 정적 메서드 모두 열거 불가

클래스 정의

클래스 이름은 파스칼 케이스

class Person {} // 클래스 선언문, 가장 일반적

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

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

클래스를 표현식으로 정의할 수 있다 ? 클래스는 값으로 나타낼 수 있다
클래스는 함수이다
클래스 내부에는 constructor, 프로토타입 메서드, 정적 메서드 정의 가능

클래스 호이스팅

클래스는 함수로 평가되기 때문에 런타임 이전에 생성됨
클래스가 평가되어 생성된 함수 객체는 constructor이다 -> 생성자 함수로써 호출 가능
constructor는 함수 객체가 생성될 때 프로토타입도 같이 생성됨
클래스는 let, const 처럼 선언문 이전에 참조시 TDZ에 빠진다

인스턴스 생성

클래스는 생성자 함수이다
new 연산자와 함께 호출하여 인스턴스 생성
생성자 함수를 new 없이 사용하면 일반 함수로 호출되지만 클래스는 에러

메서드

클래스 몸체에는 메서드만 정의할 수 있다
but 클래스 내부에 프로퍼티를 정의할 수있는 새로운 표준 사항 등장

constructor

인스턴스를 생성하고 처기화 하기 위한 특수한 메서드

class Person{
	//생성자
   constructor(name){
   	this.name = name;
   }
}

const me = new Person('Sam');

constructor는 클래스 내에 1개만 존재 가능, 생략하면 constructor(){}
constructor 내부의 this는 앞으로 생성할 인스턴스를 가르킴
constructor 내부에는 반환문이 없어야 한다, 암묵적으로 인스턴스를 반환함
constructor는 메서드로 해석되지 않고 클래스가 평가되어 생성한 함수 객체 코드의 일부가 됨
즉 클래스 정의가 평가되면 constructor의 기술된 동작을 하는 함수 객체가 생성됨

프로토타입 메서드

클래스 내부에 정의한 메서드는 프로토타입 메서드이다

class Person{
	//생성자
   constructor(name){
   	this.name = name;
   }
   
   sayHi(){
   	console.log(`i am ${this.name}`);
   }
}

const me = new Person('Sam');
me.say(); // i am Sam

정적 메서드

메서드 선언할 때 static 키워드 붙이면 정적 메서드

class Person{
	//생성자
    constructor(name){
    	this.name = name;
    }
    
    static sayHi(){
    	console.log(`Hi`);
    }
}

Person.sayHi(); 정적 메서드는 인스턴스 없이 바로 호출
const me = new Person('Sam');
me.sayHi(); // 에러, me의 프로토타입 체인에 sayHi()는 없다 

클래스는 함수 객체로 평가되니까 자신의 프로퍼티(정적 메서드)를 갖는다

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

new 연산자와 함께 클래스는 호출하면 클래스의 내부 메서드 [[Construct]]가 호출되며 인스턴스 생성

  1. 인스턴스 생성과 this 바인딩
    빈 객체가 생성되고 this에 바인딩 됨
  2. 인스턴스 초기화
    constructor 내부 코드가 실행, 인스턴스(1.에서 생성한 빈 객체)에 프로퍼티 추가
  3. 인스턴스 반환
    this를 반환 this엔 1.2. 과정을 거친 인스턴스가 바인딩되어있음

프로퍼티

인스턴스 프로퍼티

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

클래스 필드 정의 제안

클래스 필드 = 클래스가 생성할 인스턴스의 프로퍼티
ES2022 부터 클래스 필드 정의 구현
클래스 필드를 클래스 몸체에 정의 가능

class Person{
	name = 'Sam'; // 초기값 없을 시 undefined
}

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

인자를 받아서 값 세팅 해주려면 constructor 내부에서 해줘야한다
인스턴스 생성 시 인자로 세팅해줄 필요가 있으면 constructor 밖에 클래스 필드 선언하면 바보

class Person{
	name;
    
    constructor(name){
    	this.name = name; // 클래스 필드 참조할 때 this 필수
    }
}

const me = new Person('Sam');

함수를 클래스 필드에 할당하면 인스턴스 메서드가 된다
모든 클래스 필드는 인스턴스의 프로퍼티가 되기 때문이다
클래스 필드에 함수 할당은 권장X

class Person{
	getName = function(){
    	return this.name;
    }
    
    constructor(name){
    	this.name = name; // 클래스 필드 참조할 때 this 필수
    }
}

const me = new Person('Sam');
me.getName(); // 'Sam'

private 필드 정의

원래는 private,public,protected 같은 접근 제한자를 지원하지 않지만
ES2022부터 가능

#을 붙여주면 됨, 참조할 때도 #
constructor 내부에 정의하면 에러남, 클래스 몸체에 정의해야 함

class Person{
	#name = '';
    
    constructor(name){
    	this.#name = name;
    }
}

const me = new Person('Sam');
console.log(me.#name); // private필드는 클래스 내부에서만 참조 가능

static 필드 정의

ES2022부터
static 키워드로 정적 필드도 정의할 수 있다

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

console.log(MyMath.PI);

상속에 의한 클래스 확장

기존 클래스를 상속받아 새로운 클래스를 확장하여 정의

class Animal{
	constructor(age,weight){
    	this.age = age;
        this.weight = weight;
    }
    
    eat(){return 'eat';}
    
    move(){return 'move';}
}

class Bird extends Animal{
	fly(){return 'fly';}
}

const bird = new Bird(1,5);

extends 키워드

수퍼클래스와 서브클래스 간의 상속 관계를 설정
클래스도 프로토타입을 통해 상속 관계를 구현

프로토타입 메서드와 함께 정적 메서드도 상속됨

동적 상속

extends 키워드는 [[Construct]] 내부 메서드를 갖는 함수 객체로 평가되는 모든 표현식을 상속받을 수 있음

서브클래스의 constructor

서브 클래스에서 constructor를 생략하면 수퍼클래스의 constructor를 호출하는 코드가 암묵적으로 정의

constructor(...args){ super(...args);}

super()는 수퍼클래스의 constructor를 호출하여 인스턴스를 생성함

super 키워드

super는 호출할 수도 있고 식별자처럼 참조할 수도 있다

  • 호출 : 수퍼클래스의 constructor를 호출한다
  • 참조 : 수퍼클래스의 메서드를 호출할 수 있게 해줌

super 호출

  • 서브클래스에서 constructor 생략 안하면 반드시 super 호출해야함
  • super 호출 전까지 서브클래스의 constructor에서는 this 참조 불가
  • super는 서브 클래스의 constructor에서만 호출, 다른곳에선 에러남
class Base{
	constructor(a,b){
    	this.a = a;
        this.b = b;
    }
}

class Derived extends Base{
	constructor(a,b,c){
    	super(a,b);
        this.c = c;
    }
}

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

super 참조

서브클래스의 메서드 내부에서 super를 참조하여 수퍼클래스의 메서드 호출 가능
super : super를 참조한 메서드를 갖고있는 객체의 프로토타입을 가르킴

class Base{
	say(){return 'say';}
}

class Derived extends Base{
	say(){
    	return super.say();
    }
}

super는 Derived의 say 메서드에서 참조되고 있다
say 메서드는 Derived.prototype의 메서드이기때문에
Derived.prototype의 프로토타입인 Base.prototype을 가르킨다

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

  1. 서브클래스의 super호출
    서브클래스는 자신이 직접 인스턴스를 생성하지 않고 수퍼클래스에게 시킴
  2. 수퍼클래스의 인스턴스 생성과 this 바인딩
    인스턴스는 수퍼클래스가 생성하지만 new.target은 서브클래스를 가르키고 있음
    인스턴스를 서브클래스가 생성한 것으로 처리
    생성된 인스턴스의 프로토타입은 서브클래스의 prototype프로퍼티가 가르키는 객체임
  3. 수퍼클래스 인스턴스 초기화
  4. 서브클래스 constructor로 복귀와 this 바인딩
  5. 서브클래스 인스턴스 초기화
  6. 최종적으로 생성된 인스턴스 반환

이웅모, 『모던 자바스크립트 Deep Dive』, 위키북스(2021)

profile
grindin'

0개의 댓글