자바스크립트는 프로토타입 기반 객체지향 언어입니다. ES5에서는 클래스 없이도 다음과 같이 생성자 함수와 프로토타입을 통해 객체지향 언어의 상속을 구현할 수 있습니다.
ES6의 클래스가 기존의 프로토타입 기반 객체지향 모델을 폐지하고 새롭게 클래스 기반 객체지향 모델을 제공하는 것은 아닙니다.
하지만, 클래스(class)와 생성자 함수(function)는 모두 프로토타입 기반의 인스턴스를 생성하지만 다음 상황에서 동일하게 작동하지 않습니다.
1. new 연산자 호출방식
2. extends와 super 키워드 제공 여부
3. 호이스팅 동작
4. strict 지정 여부
5. 프로퍼티 어트리뷰트 열거 가능
자바스크립트의 클래스는 일급객체입니다. 더 자세히 말하면 자바스크립트의 클래스는 함수입니다.
클래스는 크게 클래스 선언식과 클래스 표현식 두 가지 방식으로 선언할 수 있습니다.
클래스 몸체에는 0개 이상의 메서드만 정의할 수 있으며, 클래스 몸체에서 정의할 수 있는 메서드는 constructor(생성자), 프로토타입 메서드, 정적 메서드 이렇게 3가지가 있습니다.
class Student {
// 생성자
constructor(name) {
this.name = name;
}
// 프로토타입 메서드
sayHi() {
console.log(`Hi ${this.name}!`);
}
// 정적 메서드
static sayHello() {
console.log(`Hello!`);
}
}
정적 메서드와 프로토타입 메서드는 다음의 차이가 있습니다.
- 자신이 속해 있는 프로토타입 체인이 달라 호출 방식이 다릅니다.
- 정적 메서드는 클래스로, 프로토타입 메서드는 인스턴스로 호출합니다.
- 정적 메서드는 인스턴스 프로퍼티를 참조할 수 없지만, 프로토타입 메서드는 가능합니다.
클래스는 생성자 함수이며 new 연산자와 함께 호출되어 인스턴스를 생성한다.
클래스는 재정의될 수 없습니다. 재정의를 시도하면 SyntaxError 가 발생합니다.
// 클래스 선언문
class Rectangle {
// ...
};
클래스 표현식이란, class를 정의하는 또 다른 방법입니다.
Class 표현식은 이름을 가질 수도 있고, 갖지 않을 수도 있습니다.
이름을 가진 class 표현식의 이름은 클래스 body의 local scope에 한해 유효합니다.
// unnamed
let Rectangle = class {
// ...
};
console.log(Rectangle.name);
// 출력: "Rectangle"
// named
let Rectangle = class Rectangle2 {
// ...
};
console.log(Rectangle.name);
// 출력: "Rectangle2"
[1] 클래스선언식 호이스팅
const Name = '';
{
// 호이스팅이 일어나지 않았다면 ''이 출력되어야 한다.
console.log(Name); // ReferenceError: Cannot access 'Name' before initialization
class Name {
constructor(name) {
this.name = name;
}
}
}
클래스선언문은 생성단계에서 선언문을 실행할때, class 키워드로 선언한 Name이란 객체는 선언과 동시에 uninitialized의 값으로 초기화된 상태로 환경 레코드에 기록됩니다. 이는 실행단계에서 Name을 콘솔로 호출하게 될 때 uninitialized과 비교하여 Reference Error를 발생시키게 됩니다. 이는 let, const의 변수 호이스팅처럼 일시적 사각지대에 영향을 받아 호이스팅이 발생하지 않은 것처럼 보이나 실제론 호이스팅이 발생한 현상입니다.
[2] 클래스표현식 호이스팅
let myName = new Name('YUN'); // Reference Error
console.log(myName);
let Name = class {
constructor(name) {
this.name = name;
}
}
클래스표현식은 Name
이란 클래스가 변수로서 호이스팅 작동하며, 이를 클래스로서 호출하게 되면 클래스가 아닌 변수로 불러오게 되어 타입에러를 발생시킵니다.
extends 키워드는 클래스 선언이나 클래스 표현식에서 다른 클래스의 자식 클래스를 생성하기 위해 사용됩니다.
하위클래스에 constructor가 있다면, "this"를 사용하기 전에 가장 먼저 super()를 호출해야 합니다.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Cat extends Animal {
constructor(name) {
super(name); // super class 생성자를 호출하여 name 매개변수 전달
}
speak() {
console.log(`${this.name} meows.`);
}
}
let d = new Cat('Persian');
d.speak(); // Persian meows.
super 키워드는 객체의 부모가 가지고 있는 메서드를 호출하기 위해 사용됩니다.
아래 코드와 같이 es5에서 사용되던 생성자 함수와 프로토타입을 통하여 클래스 확장할 수도 있습니다.
function Animal (name) {
this.name = name;
}
Animal.prototype.speak = function () {
console.log(`${this.name} makes a noise.`);
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.
// 유사한 메서드의 경우, 자식의 메서드가 부모의 메서드보다 우선합니다
1. 자바스크립트는 프로토타입 기반 객체지향 언어지만 프로토타입 기반 클래스 키워드를 제공합니다. 하지만 클래스는 단순히 설탕적 문법이 아닌 새로운 객체 생성 메커니즘입니다.
2. 자바스크립트의 클래스는 일급 객체입니다. 더 정확히는 함수입니다.
3. 클래스 몸체에서 생성할 수 있는 메서드는 constructor, 프로토타입 메서드, 정적 메서드입니다.
4. constructor는 인스턴스를 생성하고 초기화하기 위한 특수한 메서드입니다.
5. 프로토타입 메서드는 인스턴스를 통해 호출합니다.
6. 정적 메서드는 static이란 키워드로 정의한 메서드로, 클래스를 통해 호출합니다.
7. 클래스는 선언식과 표현식으로 정의하며, 표현식은 정확히 호이스팅이 발생하지 않으며, 선언식은 호이스팅이 발생하나 일시적 사각지대에 빠져 호이스팅이 일어나지 않은 것처럼 보입니다.
8. 다른 클래스의 자식 클래스를 생성하기 위해 extends 키워드를 사용하여 상속받습니다.
9. 하위클래스에 constructor가 있다면, "this"를 사용하기 전에 가장 먼저 super()를 호출해야 합니다.