ES5까지 자바스크립트에는 클래스가 없었다. 그래서 프로토타입 체이닝을 통해 클래스와 비슷한 구조를 만들었었다.
그러나 ES6부터 자바스크립트에도 클래스가 추가되었다!
클래스는 "특별한 함수"이다. 함수와 같이 class 표현과 class 선언 두 가지 방법으로 정의할 수 있다.
class
키워드와 클래스 이름 사용
class Rectangle{
constructor(height, width){
this. height = height;
this.width = width;
}
}
let Rectangle = class {
constructor(height, width){
this.height = height;
this.width = width;
}
};
console.log(Rectangle.name); // "Rectangle"
이름을 가진 클래스 표현식의 이름은 클래스 body의 local scope에 한헤 유효하다. 그러나 클래스의 name
속성을 통해 찾을 수 있다.
let Rectangle = class Rectangle2 {
constructor(height, width){
this.height = height;
this.width = width;
}
};
console.log(typeof Rectangle); // "function"
console.log(typeof Rectangle2); // "undefined"
console.log(Rectangle.name); // "Rectangle2"
이 부분이 잘 이해가 안되는데...
클래스 선언의 경우 let
으로 정의한 외부 바인딩은 const
로 정의한 내부 바인딩과 동일한 이름(Rectangle
)을 가지게 된다.
이름이 부여된 클래스 표현식에서 Rectangle2
는 const
정의 내에만 존재하기 때문에 클래스 내부에서만 사용할 수 있다고 한다. 클래스 밖에서는 Rectangle2
바인딩이 존재하기 않기 때문에 undefined
이다.
class Rectangle{
constructor(height, width){
this. height = height;
this.width = width;
} // body
}
클래스의 본문(body)은 strict mode
에서 실행된다. 성능 향상을 위해 더 엄격한 문법이 적용된다는 뜻이다.
class Person {
constructor(name){ // constructor는 이름을 바꿀 수 없다.
// this는 클래스가 생성할 인스턴스를 가리킨다.
// _name은 클래스 필드이다.
this._name = name;
}
}
constructor
는 인스턴스를 생성하고 클래스 필드를 초기화하기 위한 특수한 메서드이다.
constructor
는 클래스 안에 한 개만 존재할 수 있다. 2개 이상 있을 경우 Syntax Error가 발생하니까 주의하자.
부모 클래스의 constructor
를 호출해야할 경우에는 super
키워드를 사용하면 된다.
클래스 필드의 선언과 초기화는 반드시 constructor
내부에서 실시한다.
constructor
내부에 선언한 클래스 필드는 클래스가 생성할 인스턴스에 바인딩한다. 클래스 필드는 그 인스턴스의 프로퍼티가 되며, 인스턴스를 통해 클래스 외부에서 언제나 참조할 수 있다. (public)
자바스크립트 클래스에서는 인스턴스에 상속되는지 여부에 따라서 메소드를 프로토타입 메서드, 스태틱 메서드로 나눈다.
인스턴스에 상속되어 인스턴스에서 직접 호출해 사용할 수 있는 메서드
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea();
}
// 메서드
calcArea() {
return this.height * this.width;
}
}
const square = new Rectangle(10, 10);
console.log(square.area); // 100
인스턴스에 상속되므로 Rectangle
클래스를 이용하여 생성된 square
인스턴스에서 area
라는 메서드를 호출해서 사용할 수 있다.
인스턴스에서 호출할 수 없는 메서드
정적 메서드는 클래스의 인스턴스가 아닌 클래스 이름으로 호출한다. 인스턴스를 생성하지 않아도 호출할 수 있다.
이것은 또한, 정적 메소드는 this
를 사용할 수 없다는 의미도 된다. 일반 메소드 내에서 this
는 앞으로 만들어질 클래스의 인스턴스를 가리키는데 정적 메서드는 인스턴스가 아닌 클래스 이름으로 호출하기 때문이다.
달리 말하면, 메서드 내부에서 this
를 사용할 필요가 없는 메서드는 정적 메서드로 만들 수 있다는 이야기이다. Math
객체의 메서드처럼 애플리케이션 전역에서 사용할 유틸리티 함수를 생성할 때 주로 사용된다.
일반 클래스 프로퍼티와 유사하게 생겼으나 앞에 static이 붙는다는 점만 다르다.
class Article {
static publisher = "David Kantor"
}
console.log(Article.publisher); // David Kantor
프로토타입 메서드나 정적 메서드가 this
값 없이 호출될 때, this
값은 메서드 안에서 undefined가 된다. class 문법 안에 있는 코드는 항상 strict mode
로 실행되기 때문에 "use strict" 명령어 없이도 그렇게 동작한다.
class Animal {
speak() {
return this;
}
static eat() {
return this;
}
}
let obj = new Animal();
obj.speak(); // the Animal object
let speak = obj.speak;
speak(); // undefined
Animal.eat() // class Animal
let eat = Animal.eat;
eat(); // undefined
extends 키워드는 클래스를 다른 클래스의 하위 클래스로 만들기 위해 사용된다.
아래 예제에서 Dog 클래스는 Animal 클래스의 하위 클래스가 된다.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name) {
super(name); // super class 생성자를 호출하여 name 매개변수 전달
}
speak() {
console.log(`${this.name} barks.`);
}
}
let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.
super
키워드는 인스턴스의 부모가 가지고 있는 메서드를 호출하기 위해 사용한다.
위 예제 코드에서 Dog 클래스는 Animal 클래스의 하위 클래스인데, Animal 클래스의 생성자나 메서드, this를 사용하려면 super가 먼저 호출되어야 한다.
class Polygon {
constructor(height, width) {
this.name = 'Polygon';
this.height = height;
this.width = width;
}
sayName() {
console.log('Hi, I am a ', this.name + '.');
}
}
class Square extends Polygon {
constructor(length) {
this.height; // 참조오류가 발생합니다. super가 먼저 호출되어야 합니다.
// 여기서, 부모클래스의 생성자함수를 호출하여 높이값을 넘겨줍니다.
// Polygon의 길이와 높이를 넘겨줍니다.
super(length, length);
// 참고: 파생 클래스에서 super() 함수가 먼저 호출되어야
// 'this' 키워드를 사용할 수 있습니다. 그렇지 않을 경우 참조오류가 발생합니다.
this.name = 'Square';
}
}
클래스는 재정의될 수 없다. 재정의를 시도하면 Syntax Error가 발생한다.