25장 클래스
25.1 클래스는 프로토타입의 문법적 설탕인가?
JS는 프로토타입 기반 객체지향 언어다.
ES5에서는 클래스 없이 생성자 함수와 프로토타입으로 상속
을 구현했다. 하지만 클래스 기반 언어에 익숙한 프로그래머들에겐 프로토타입 기반 프로그래밍이 JS의 진입 장벽이 되었다.
ES6에서 도입된 클래스
는 클래스 기반 객체지향 프로그래밍 언어와 매우 흡사한 객체 생성 매커니즘을 제시했다. 하지만 이 클래스는 함수이며 기존 프로토타입 기반 패턴을 클래스 기반 패턴처럼 사용할 수 있는 문법적 설탕
이라 볼 수 있다.
클래스는 생성자 함수와 매우 유사하게 동작하지만 몇 가지 차이가 있다.
class는 new 연산자 없이 호출하면 에러가 발생한다.
class는 상속을 지원하는 extends와 super 키워드를 제공한다.
class는 호이스팅이 발생하지 않는 것처럼 동작한다.
class 내 모든 코드에는 암묵적으로 strict mode가 지정되어 실행되며 해제할 수 없다.
class의 constructor, 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 false다. 즉 열거되지 않는다.
생성자 함수와 클래스는 프로토타입 기반의 객체지향을 구현했다는 점에서 매우 유사하다. 하지만 클래스는 더 견고하고 명료하다. 특히, extends와 super 키워드는 상속 관계 구현을 더욱 간결하고 명료하게 한다.
따라서 클래스가 문법적 설탕이라 보기보다 새로운 객체 생성 매커니즘으로 보는게 합당하다.
25.2 클래스 정의
class 키워드를 사용해 정의한다. 파스칼 케이스를 사용하는 것이 일반적.
클래스는 함수다. 따라서 클래스는 값처럼 사용할 수 있는 일급 객체
다.
따라서 표현식으로 정의할 수도 있다. 함수와 마찬가지로 이름을 갖거나 갖지 않을수 있다.
일급 객체로서 다음과 같은 특징을 갖는다.
무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다.
변수나 자료구조(객체, 배열 등)에 저장할 수 있다.
함수의 매개변수에 전달할 수 있다.
함수의 반환값으로 사용할 수 있다.
클래스 몸체에는 메서드만 정의할 수 있는데, 정의 가능한 메서드는 constructor(생성자), 프로토타입 메서드, 정적 메서드 세 가지가 있다.
클래스와 생성자 함수의 정의 방식은 형태적인 면에서 매우 유사하다.
25.3 클래스 호이스팅
클래스는 함수로 평가된다.
클래스 선언문으로 정의한 클래스는 함수 선언문과 같이 런타임 이전에 먼저 평가되어 함수 객체를 생성한다. 이때 평가되어 생성된 함수 객체는 생성자 함수로서 호출할 수 있는 함수, constructor다. (프로토타입도 더불어 생성된다.)
클래스는 클래스 정의 이전에 참조할 수 없다.
클래스 선언문은 마치 호이스팅이 발생하지 않는 것처럼 보이나 그렇지 않다.
클래스 선언문도 변수 선언, 함수 정의와 같이 호이스팅이 발생한다. let, const 키워드로 선언한 변수처럼 호이스팅된다. 클래스 선언문 이전에 일시적 사각지대에 빠지기에 발생하지 않는 것처럼 보이는것.
25.4 인스턴스 생성
클래스는 생성자 함수이며 new 연산자와 함께 호출되어 인스턴스를 생성한다.
클래스는 인스턴스 생성이 유일한 존재 이유이므로 반드시 new 연산자와 호출해야 한다. 클래스 표현식으로 정의된 클래스를 식별자를 사용해 인스턴스를 생성하지 않고 클래스 이름으로 인스턴스를 생성하면 에러가 발생한다.
25.5 메서드
클래스 몸체에는 0개 이상의 메서드만 선언할 수 있다. 정의할 수 있는 메서드는 constructor, 프로토타입, 정적 메서드 총 3가지이다.
25.5.1 constructor
constructor는 인스턴스를 생성하고 초기화하기 위한 특수한 메서드다. - 이름 변경 불가능
constructor는 함수 객체 코드의 일부가 된다. 클래스 내 최대 1개만 존재할 수 있으며, 생략시 빈 객체가 암묵적으로 정의된다.
프로퍼티가 추가되어 초기화된 인스턴스를 생성하려면 constructor 내부의 this에 인스턴스 프로퍼티를 추가하면 된다. 클래스 외부에서 초기값을 전달하려면 constructor에 매개변수를 선언하고, 인스턴스를 생성할 때 초기값을 전달하면 된다. 인스턴스를 초기화하려면 constructor를 생략해서는 안된다.
또한 constructor는 별도의 반환문을 갖지 않아야 한다. new 연산자와 함께 클래스가 호출되면 생성자 함수와 동일하게 암묵적으로 this, 인스턴스를 반환하기 때문이다. 만약, 명시적으로 다른 객체를 반환하면 명시한 객체가 반환된다. ( 명시적으로 원시값을 반환하면 무시됨 ) 따라서 반드시 constructor 내부에서 return 문을 반드시 생략해야 한다.
25.5.2 프로토타입 메서드
클래스 몸체에서 정의한 메서드는 생성자 함수에 의한 객체 생성 방식과 다르게 클래스의 prototype 프로퍼티에 메서드를 추가하지 않아도 기본적으로 프로토타입 메서드가 된다.
프로토타입 체인은 기본의 모든 객체 생성 방식뿐 아니라 클래스에 의해 생성된 인스턴스에도 동일하게 적용된다. 결국 클래스는 생성자 함수와 같이 인스턴스를 생성하는 생성자 함수라 볼 수 있다.
즉, 클래스는 프로토타입 기반의 객체 생성 메커니즘이다.
25.5.3 정적 메서드
정적 메서드
는 인스턴스를 생성하지 않아도 호출 할 수 있는 메서드를 말한다.
클래스에서는 메서드에 static 키워드를 붙이면 정적 메서드가 된다.
25.5.4 정적 메서드와 프로토타입 메서드의 차이
정적 메서드와 프로토타입 메서드는 속해있는 프로토타입 체인이 다르다
정적 메서드는 클래스로 호출하고 프로토타입 메서드는 인스턴스로 호출한다.
정적 메서드는 인스턴스 프로퍼티를 참조할 수 없지만 프로토타입 메서드는 가능하다.
정적 메서드는 인스턴스를 사용하지 않을때 !, this를 사용하지 않을때!
프로토타입 메서드는 인스턴스를 사용할때 !, this를 사용해야할때 !
25.5.5 클래스에서 정의한 메서드의 특징
function 키워드를 생략한 메서드 축약 표현을 사용한다.
객체 리터럴과 다르게 클래스에 메서드를 정의할 때는 콤마가 필요없다.
암묵적으로 strict mode로 실행된다.
열거할 수 없다. (for in 문이나 Object.keys 메서드 등)
내부 메서드 [[Construct]]를 갖지 않는 non-constructor다. 따라서 new 연산자와 함께 호출할 수 없다.
25.6 클래스의 인스턴스 생성 과정
new 연산자와 함께 클래스를 호출하면 클래스의 내부 메서드 [[Construct]] 가 호출된다. 그리고 다음과 같은 과정을 거쳐 인스턴스가 생성된다.
1️⃣ 인스턴스 생성과 this 바인딩
constructor 내부 코드가 실행되기 전 암묵적으로 빈 객체(클래스가 생성한 인스턴스)가 생성된다.
이때 클래스가 생성한 인스턴스의 프로토타입으로 클래스의 prototype 프로퍼티가 가리키는 객체가 설정된다. 그리고 암묵적으로 인스턴스는 this에 바인딩된다.
따라서 constructor 내부의 this는 클래스가 생성한 인스턴스를 가리키게 된다.
2️⃣ 인스턴스 초기화
constructor 내부 코드가 실행되어 this에 바인딩되어 있는 인스턴스를 초기화한다.
this에 바인딩되어 있는 인스턴스에 프로퍼티를 추가하고 constructor가 인수로 받은 초기값으로 인스턴스의 프로퍼티 값을 초기화한다. (constructor가 생략되면 이 과정도 생략됨)
3️⃣ 인스턴스 반환
클래스의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this를 암묵적으로 반환된다.
25.7 프로퍼티
25.7.1 인스턴스 프로퍼티
인스턴스 프로퍼티는 constructor 내부에서 정의한다.
접근 제한자를 지원하지 않기에 언제나 public하다.
25.7.2 접근자 프로퍼티
다른 데이터 프로퍼티의 값을 읽거나 저장할때 사용하는 접근자 함수로 구성된 프로퍼티
getter는 참조하기에 반드시 반환해야하고, setter는 할당하기에 반드시 매개변수가 있어야 한다.
클래스의 메서드는 기본적으로 프로토타입 메서드가 된다.
따라서 클래스의 접근자 프로퍼티 또한 프로토타입의 프로퍼티가 된다.
25.7.3 클래스 필드 정의 제안
(내용만 보면 이해 안되는데 예시를 보면 이해됨… 책 예시를 보자 !)
클래스 필드
- 클래스 기반 객체지향 언어에서 클래스가 생성할 인스턴스의 프로퍼티를 가르키는 용어.
최신 브라우저와 node.js에서 사용 가능함
25.7.4 private 필드 정의 제안
JS는 캡슐화를 완전히 지원하지 않는다. 접근 제한자를 지원하지 않기에 인스턴스 프로퍼티는 언제나 public이다.
최신 브라우저와 node.js에서 사용 가능함
클래스 몸체에서 private 필트의 선두에 #을 붙여 사용한다. 참조시에도 #을 붙임
클래스 내부에서만 참조할 수 있고, 반드시 클래스 몸체에 정의해야 한다.
(접근자 프로퍼티로 간접 접근할 수 있음..!)
25.7.5 static 필드 정의 제안
static 키워드를 사용해 정적 메서드를 정의할 수 있다.
최신 브라우저와 node.js에서 사용 가능
25.8 상속에 의한 클래스 확장
25.8.1 클래스 상속과 생성자 함수 상속
상속에 의한 클래스 확장
은 기존 클래스를 상속받아 새로운 클래스를 확장하여 정의하는 것.
extends 키워드를 사용해 상속할 수 있다.
25.8.2 extends 키워드
상속을 통해 클래스를 확장하려면 extends 키워드를 사용해 상속받을 클래스를 정의한다.
extends 키워드는 수퍼 클래스와 서브 클래스 간 상속 관계를 설정한다. (프로토타입을 통해 상속 관계를 구현한다.)
수퍼 클래스와 서브 클래스는 인스턴스의 프로토타입 체인과 클래스 간 프로토타입 체인도 생성한다. 이를 통해 프로토타입 메서드와 정적 메서드 모두 상속이 가능하다.
25.8.3 동적 상속
extends 키워드는 클래스 뿐 아니라 생성자 함수를 상속받아 클래스를 확장할 수 있다. 단, extends 키워드 앞에는 반드시 class가 와야 한다.
extends 키워드 다음에는 [[Construct]] 내부 메서드를 갖는 모든 표현식을 사용할 수 있다. 이를 통해 동적으로 상속받을 대상을 결정할 수 있다.
25.8.4 서브클래스의 constructor
클래스에서 constructor를 생략하면 비어있는 constructor가 암묵적으로 정의된다.
args
는 new 연산자와 함께 클래스를 호출할 때 전달한 인수의 리스트다.
super()
는 수퍼클래스의 constructor를 호출하여 인스턴스를 생성한다.
25.8.5 super 키워드
super
키워드는 함수처럼 호출할 수도 있고, 식별자처럼 참조할 수 있는 특수한 키워드다.
super 호출
super를 호출하면 수퍼클래스의 constructor를 호출한다.
수퍼클래스의 프로퍼티를 그대로 갖는 인스턴스라면 서브클래스의 constructor를 생략할 수 있다.
하지만, 수퍼클래스의 프로퍼티와 서브클래스에서 추가한 프로퍼티를 갖는 인스턴스를 생성한다면 constructor를 생략할 수 없다. 수퍼클래스의 constructor에 전달할 필요가 있는 인수는 super를 통해 전달한다.
이처럼 인스턴스 초기화를 위해 전달한 인수는 수퍼클래스와 서브클래스에 배분되고, 상속 관계의 두 클래스는 서로 협력하여 인스턴스를 생성한다.
super 호출시 주의사항
서브클래스에서 constructor를 생략하지 않는 경우, 서브 클래스의 constructor에 반드시 super를 호출해야 한다.
서브클래스의 constructor에서 super를 호출하기 전에는 this를 참조할 수 없다.
super는 반드시 서브클래스의 constructor에서만 호출한다.
super 참조
메서드 내에서 super를 참조하면 수퍼클래스의 메서드를 호출할 수 있다.
책으로 확인..! p.457 ~ p.460
25.8.6 상속 클래스의 인스턴스 생성 과정
서브클래스의 super 호출 ㄴ JS 엔진은 클래스를 평가할 때 내부 슬롯 [[ConstructorKind]]에 수퍼와 서브를 구분한다. ㄴ 수퍼 클래스는 인스턴스를 생성하고 this를 반환한다. ㄴ 서브 클래스는 인스턴스 생성을 수퍼 클래스에 위임한다. ㄴ 서브의 constructor에 반드시 super를 호출해야하는 이유 !!
수퍼클래스의 인스턴스 생성과 this 바인딩 ㄴ 생성된 인스턴스에 this 바인딩 ㄴ 수퍼 클래스가 생성했지만 서브 클래스가 생성한 인스턴스로 처리된다.
수퍼클래스의 인스턴스 초기화 ㄴ 수퍼의 constructor가 실행되어 this에 바인딩된 인스턴스를 초기화한다.
서브클래스 constructor 복귀와 this 바인딩 ㄴ super의 호출이 종료되고 반환한 인스턴스가 this에 바인딩된다. 이 인스턴스에 this를 바인딩해 그대로 사용한다. ㄴ 서브의 constructor에서 super 호출 전 this를 참조할 수 없는 이유 !!
서브클래스 인스턴스 초기화 ㄴ super 호출 이후 서브의 constructor에 기술된 인스턴스 초기화가 실행된다.
인스턴스 반환 ㄴ 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
25.8.7 표준 빌트인 생성자 함수 확장
extends 키워드 다음에는 클래스뿐 아니라 [[Construct]] 내부 메서드를 갖는 모든 표현식을 사용할 수 있다. String, Number, Array 같은 표준 빌트인 객체도 사용할 수 있다.
Last updated
Was this helpful?