동일한 종류의 여러 객체를 생성하기 위해서,
생성자 함수와 new 연산자를 통해 만들고, prototype 프로퍼티를 통해 상속할 수 있었습니다.
ES6에서 도입된 클래스 문법을 사용하면, OOP에서 사용하는 클래스 기능을 JS에서 사용할 수 있습니다.
생성문법
class MyClass {
constructor() {...}
method1() {...}
method2() {...}
method3() {...}
}
생성후에 new MyClass()
를 호출하면, 정의한 메서드가 포함 된 객체가 생성됩니다.
constructor
는 new에 의해 자동 호출되어 객체를 초기화 할 수 있습니다.
자바스크립트에서 클래스는 실제로는 함수의 한 종류입니다.
class MyClass {}
alert(typeof MyClass) // function
class 문법이 하는 일
1. MyClass
라는 이름의 함수를 만듭니다. 본문을 constructor
에서 가져옵니다.
2. class내에서 정의한 메서드를 MyClass.prototype
에 저장합니다.
따라서, new MyClass
로 생성된 객체는 메서드를 사용할 수 있습니다.
하는일이 위와 같다면, 생성자 함수와의 차이가 없다고 느낍니다.
그래서, 편의 문법 정도로 생각하기도 합니다.
하지만, 몇가지 차이가 있습니다.
new
로만 호출할 수 있습니다.생성자함수는 new
가 아니더라도 호출이 가능합니다.
하지만, 클래스로 만든 함수에는 내부 프로퍼티 [[FunctionKind]]:classconstructor
가 붙습니다.
자바스크립트는 이 프로퍼티를 확인하여, new
와 함께 호출하지 않으면 에러가 발생하도록 합니다.
클래스의 프로토타입에 추가된 메서드의 enumerable
플래그는 false
입니다.
이러한 차이가 있어 단지 편의 문법이 아니라는 것을 알 수 있습니다.
함수처럼 클래스도 다른 표현식 내부에서 정의, 전달, 반환 할당할 수 있습니다.
표현식
let User = class{
...
}
때문에, 기명 함수 표현식과 유사하게 클래스 표현식에도 이름을 붙힐 수 있습니다.
let User = class Myclass{
...
}
동적으로 선언하거나, 반환할 수 있습니다.
리터럴 객체처럼, 클래스도 getter와 setter를 포함할 수 있습니다.
class User{
get name() {
return this._name;
}
set name(value) {
if(value.length <4) {
alert('너무 짧습니다')
}
this._name = value;
}
}
[]
를 사용해 computed method name을 만들 수 있습니다.
class User() {
[Symbol.xxxx]() { ... }
}
프로토타입 상속처럼, 클래스도 기존 클래스를 확장하여 만들려고 할 때, 상속을 사용합니다.
extends
class Animal{
constructor(name) {
this.speed = 0;
this.name = name;
}
run(speed) {
this.speed = speed;
alert(`${this.name} 은/는 속도 ${this.speed}로 달립니다.`);
}
stop() {
this.speed = 0;
alert(`${this.name} 이/가 멈췄습니다.`);
}
}
새로 만들 클래스 Rabbit
은 Animal
을 확장하여 만듭니다.
이러면 Animal
의 기능을 모두 할 수 있습니다.
class Rabbit extends Animal {
hide() {
alert(`${this.name} 이/가 숨었습니다!`);
}
}
클래스 Rabbit
은 hide
메서드에도, run
같은 Animal
메서드에도 접근할 수 있습니다.
extends
뒤에 표현식이 올 수 있습니다.
부모 클래스를 만들어주는 함수를 호출할 수 있습니다.
위 처럼 클래스 Rabbit
은 Animal
의 메서드를 사용할 수 있습니다.
그런데, Rabbit
에서도 run
과 같은 메서드를 정의하면, 상속받은 메서드가 아닌 자체 메서드가 사용됩니다.
class Rabbit extends Animal {
hide() {
alert(`${this.name} 이/가 숨었습니다!`);
}
run() {
...
}
}
개발을 하다보면, 부모 메서드의 기능에서 일부를 확장하고 싶을 수 있습니다.
이때 super
를 사용합니다.
super
는 부모클래스에 정의된 메서드를 호출합니다.super(...)
은 부모 생성자를 호출하며, 자식 생성자에서만 사용합니다.class Rabbit extends Animal {
hide() {
alert(`${this.name} 이/가 숨었습니다!`);
}
stop() {
super.stop();
this.hide();
}
}
상속받은 클래스가 생성자가 없으면, 비어있는 생성자가 만들어집니다.
class Rabbit extends Animal{
constructor(...args) {
super(...args)
}
}
상속 받은 클래스도 생성자를 정의하면, 오버라이딩됩니다.
다만, 상속 받은 클래스의 생성자에서는 반드시 super(...)
가 호출되어야 합니다.
super(...)
은 this
이전에 호출해야 합니다.
클래스 생성자 함수의 내부 프로퍼티를 통해 상속 여부를 구분합니다. [[ConstructorKind]]:"derived"
기존 생성자 함수를 호출하면, 빈 객체를 만들고, this
에 할당하는 일을,
상속 받은 생성자 함수에서는 부모의 클래스가 처리하길 기대합니다.
때문에, this
이전에 super()
를 먼저 호출해야 합니다.