시대의 명작 코어 자바스크립트를 읽고 자바스크립트 마스터에 도전합니다...
자바스크립트는 프로토타입 기반 언어라서 '상속' 개념이 존재하지 않는다. 클래스 기반의 언어에 익숙한 많은 개발자들을 혼란케 했고, 이러한 불편함을 해결하기 위해 ES6에는 클래스 문법이 추가되었다. 다만 ES6의 클래스에서도 일정 부분은 프로토타입을 활용하고 있기 때문에, ES5에서 클래스를 흉내내기 위한 구현 방식을 학습하는 것은 큰 의미가 있다.
일반적인 개념으로서의 클래스
음식(superClass) - 과일(subClass/superClass) - 귤류(subClass) - 오렌지(element)
- 하위클래스는 아무리 구체화되더라도 결국 추상적인 개념일 뿐이다.
- 오렌지와 같이 어떤 클래스의 속성을 지니는 실존하는 개체를 인스턴스라고 한다.
- 클래스는 인스턴스들로부터 공통점을 발견하여 정의된다.
- 하나의 개체가 같은 레벨의 서로 다른 여러 클래스의 인스턴스일 수도 있다.
프로그래밍 언어상의 클래스
- 사용자가 직접 여러가지 클래스를 정의해야 한다.
- 클래스를 바탕으로 인스턴스를 만들 때 비로소 어떤 개체가 클래스의 속성을 지닌다.
- 클래스가 먼저 정의되어야만 그로부터 공통적인 요소를 지니는 개체들을 생성할 수 있다.
- 한 인스턴스는 하나의 클래스만을 바탕으로 만들어진다. 인스턴스를 생성할 때 호출할 수 있는 클래스는 오직 하나뿐일 수밖에 없기 때문이다.
- 사용하기에 따라 추상적인 대상일 수도, 구체적인 개체가 될 수도 있다.
자바스크립트는 프로토타입 기반 언어이므로 클래스의 개념이 존재하지 않지만, 프로토타입을 일반적인 의미에서의 클래스 관점에서 접근해보면 비슷하게 해석할 수 있다.
인스턴스에 상속되는지(인스턴스가 참조하는지) 여부에 따라 스태틱 멤버와 인스턴스 멤버로 나뉜다.
자바스크립트에서는 인스턴스에서도 직접 메서드를 정의할 수 있기 때문에 혼란을 야기할 수 있어서 인스턴스 메서드 대신에 프로토타입 메서드라고 부르는게 좋다.
// 생성자
var Rectangle = function(width, height) {
this.width = width;
this.height = height;
}
// (프로토타입) 메서드
Rectangle.prototype.getArea = function() {
return this.width * this.height;
}
// 스태틱 메서드
Rectangle.isRectangle = function(instance) {
return instance instanceof Rectangle && instance.width > 0 && instance.height > 0
}
var rect1 = new Rectangle(3,4)
console.log(rect1.getArea()); // 12 (O)
console.log(rect1.isRectangle(rect1)); // Error (X)
console.log(rect1.Rectangle.isRectangle(rect1)); // true
rect1.__proto__.getArea
에 접근하는 것과 같다. 따라서 정상적으로 계산값이 반환된다. 이렇게 인스턴스에서 직접 호출할 수 있는 메서드가 프로토타입 메서드이다.isReactangle
라는 메서드에 접근하고자 하지만rec1
에 해당 메서드는 없고, rect1.__proto__
에도 없으며, rect1.__proto__.__proto__
에도 없다. 결국 함수가 아니어서 실행할 수 없다는 에러가 발생한다.즉, 클래스(prototype)이 구체적인 데이터를 지니지 않게 하는 방법...
1. subClass.prototype
에 superClass
의 인스턴스를 할당한 다음 프로퍼티를 모두 삭제하는 방법
2. 빈 함수(Bridge
)를 활용하는 방법
3. Object.create
를 이용하는 방법
이전까지는 상속 및 추상화를 구현하기 위해 상당히 복잡한 방법을 사용했으나, ES6에서는 클래스 문법이 도입되어 상당히 간단하게 처리된다.
ES5와 ES6의 클래스 문법 비교
// ES5
var ES5 = function (name) {
this.name = name;
};
ES5.staticMethod = function () {
return this.name + ' staticMethod';
};
ES5.prototype.method = function () {
return this.name + ' method';
};
var es5Instance = new ES5('es5');
console.log(ES5.staticMethod()); // es5 staticMethod
console.log(es5Instance.method()); // es5 method
// ES6
var ES6 = class {
constructor (name) {
this.name = name;
}
static staticMethod () {
return this.name + ' staticMethod';
}
method () {
return this.name + ' method';
}
};
var es6Instance = new ES6('es6');
console.log(ES6.staticMethod()); // es6 staticMethod
console.log(es6Instance.method()) // es6 method
class
라는 명령어 뒤에 바로 { }
가 등장하는데, 이 중괄호 묶음의 내부가 클래스 본문 영역이다.constructor
라는 이름 뒤에 바로 ( ) {
가 등장한다. 클래스 본문에서는 function
키워드를 생략하더라도 모두 메서드로 인식한다. 이름처럼 ES5의 생성자 함수와 동일하다.static
키워드로 시작하는 메서드는 ES5에서 생성자 함수에 바로 할당하는 메서드와 동일하게 생성자 함수(클래스)만이 호출할 수 있다.method
가 바로 나오는 부분은 자동으로 prototype
객체 내부에 할당되는 메서드이다. ES5.prototype.method
와 동일하게 인스턴스가 프로토타입 체이닝을 통해 마치 자신의 것처럼 호출할 수 있는 메서드이다.