클래스는 별도 타입의 객체를 생성하하는 설계도면이라고 볼 수 있다.
마치 붕어빵 틀처럼, 클래스를 통해 객체가 가져야 할 속성과 메소드를 정의하면,
해당 클래스에서 만들어진 인스턴스들은 모두 그 행위를 할 수 있게 된다.
아래 코드 예시에서 쇼핑카트 클래스를 만들고, 그 카드에 물건을 담는 메소드를 만들어 탑재시킨 후,
몇 가지 인스턴스 카트들을 찍어내 보겠다.
class ShoppingCart {
constructor() {
this.store = {};
}
addItem(item) {
this.store[item.id] = item;
}
getItem(id) {
return this.store[id]
}
}
const cart1 = new ShoppingCart();
cart1.addItem(id: 0, name: "coffee Dripper")
console.log(cart1.store) // {0: {id: 0, name: "coffee Dripper"}}
const clone = cart1.getItem(0);
console.log(clone); // "coffee Dripper"
class 키워드를 사용해 ShoppingCart 클래스를 정의한다. 클래스명 뒤에 중괄호가 오고, 그 내부(클래스 몸통(body))을 채우는 작업을 하게 된다.
먼저 constructor 는 클래스의 생성자 함수이며, 클래스 내부에 단 하나만 존재할 수 있다. 여기에서는 정의하고 있지 않으나 필요한 경우 매개변수도 정의할 수 있고, new 키워드를 통해 객체가 생성될 때 호출되어 내부의 코드를 실행한다.
addItem 과 getItem 은 메소드를 정의한 것이다. new 키워드를 통해 생성된 객체가 공통적으로 사용할 수 있으며, 예시에서 작성한 메소드의 기능은 간단하니 설명하지 않고 넘어가겠다.
ShoppingCart 클래스의 인스턴스인 cart1 객체를 new 키워드를 사용해 생성했으며, 생성된 인스턴스 cart1 이 ShoppingCart 클래스의 메소드들을 자유롭게 사용하는 것을 볼 수 있다.
프로토타입 기반의 상속과 별반 다를게 없는 클래스 상속이 어떻게 발생하는지 아래 예시코드를 통해 살펴보자.
class Canvas {
constructor(width, height) {
this.width = width;
this.height = height;
}
draw() {
console.log('draw something');
}
}
class Eraser extends Canvas {
constructor(width, height) {
super(width, height)
}
clean() {
this.draw();
console.log(`make ${this.width} X ${this.height} canvas clean`);
}
}
const sheet1 = new Eraser(100, 100);
sheet1.clean(); // 'draw something'
// 'make 100 X 100 canvas clean'
Canvas 클래스를 정의하였다. 클래스가 호출되면 생성자 함수인 constructor 가 실행된다. 이 클래스에는 draw 메소드를 장착시켰는데, 이 메소드는 특정 문자열을 콘솔에 출력한다.
그 아래에는 Canvas 클래스를 상속받는 Eraser 클래스를 정의했다. extends 키워드를 사용하면 상속관계를 설정할 수 있다.
Eraser 클래스는 전달받은 인자를 사용해 super() 를 실행한다. super 는 자신의 부모 클래스의 생성자 함수, 여기에서는 Canvas 클래스의 constructor 를 의미한다.
Eraser 클래스에 탑재한 clean 메소드는 부모 클래스의 draw() 를 호출할 수 있다. 앞에 붙은 this 는 Eraser 클래스를 의미한다.
Eraser 클래스에 (100,100) 인자를 전달하면서 인스턴스인 sheet1 을 정의하였다. 그리고 clean 메소드를 호출하면 그 부모클래스에 들어있는 draw 메소드까지 무사히 사용하는 것을 볼 수 있다.