기술면접을 준비하는 과정에서 자바스크립트의 클래스 개념에 대해 공부하였고 클래스 개념에 대해 정리한다. 이 정리글은 코어 자바스크립트(정재남)를 참고하여 작성하였다.
ES6에서 클래스 문법이 추가되었다. 다만 ES6의 클래스에서도 일정 부분 프로토타입을 활용하기 때문에 프로토타입을 통해 클래스를 이해할 수 있다.
클래스는 어떤 구체적인 개체들의 공통 속성을 모아 정의한 개념이다.
인스턴스는 클래스의 속성을 갖는 실존하는 개체를 말한다.
책에서는 클래스 예시를 '음식', '과일'에 비유하여 설명한다.
'음식', '과일'은 어떤 사물들의 공통 속성을 모아 정의한 것일 뿐 직접 만질 수도 볼 수도 없는 추상적인 개념이다. 반면, 배, 사과, 바나나는 직접 만지고, 보고, 먹을 수 있는 구체적이고 실존하는 사물에 해당한다.
여기서 '음식', '과일'은 모두 클래스다.
하위 개념(하위 클래스)는 상위 개념(상위 클래스)를 포함하면서 더 구체적인 개념을 추가한다.
하위 클래스로 갈수록 상위 클래스의 속성을 '상속'하면서 더 구체적인 요건이 추가된다. 하위 클래스가 상위 클래스보다 구체적일지라도 결국은 추상적인 개념이다.
귤류 클래스의 감귤, 자몽, 천혜향은
감귤, 자몽, 천혜향은 모두 인스턴스다. 음식, 과일, 귤류 클래스들의 속성을 갖는 구체적인, 실존하는 개체이기 때문이다.
현실세계에서는 개체들이 이미 존재하는 상태에서 이들을 구분하기 위해 클래스를 도입한다.
프로그래밍 언어의 접근 방식은 정반대이다.
또한 하나의 인스턴스는 하나의 클래스만을 바탕으로 만들어진다.
프로그래밍 언어의 클래스는 사용하기에 따라 추상적일 수도, 구체적인 개체가 될 수도 있다.
자바스크립트는 프로토타입 기반 언어다. 따라서 클래스 개념이 존재하지 않는다.
하지만 프로토타입을 일반적인 의미에서의 클래스 관점에서 접근하면 비슷하게 해석할 수 있다.
생성자 함수 Array를 new 연산자와 함께 호출하면 인스턴스가 생성된다.
인스턴스에 상속되는지(인스턴스가 참조하는지) 여부에 따라 스태틱 멤버와 인스턴스 멤버로 나뉜다.(다른 언어의 클래스 구성 요소에 대한 정의를 차용하여 분류한 것) 하지만 인스턴스에서도 직접 메서드를 정의할 수 있기 때문에 인스턴스 멤버인 '인스턴스 메서드'는 프로토타입에 정의된 메서드를 지칭하는 것인지, 인스턴스에 정의한 메서드를 지칭하는 것인지에 대한 혼란을 야기한다.
따라서 '인스턴스 메서드' 대신 자바스크립트의 특징을 살린 '프로토타입 메서드'라고 부르는 것이 좋다.
생성자 함수 Array와 인스턴스 예시로 다시 알아보자.

생성자 함수 Array를 클래스라고 보면
예제를 통해 클래스 관점에서 바라본 프로토타입 시스템을 좀 더 살펴보자.
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
console.log(rect1.isRectangle(rect1)); // TypeError: rect1.isRectangle is not a function
console.log(Rectangle.isRectangle(rect1)); // true
Rectangle 생성자 함수를 new 연산자와 함께 호출하여 생성된 인스턴스를 rect1에 할당한다.getArea 메서드는 인스턴스가 마치 자신의 것처럼 호출할 수 있다.rect1.getArea는 사실 rect1.__proto__.getArea에 접근한다.__proto__를 생략하므로 this가 rect1인 상태로 실행된다.rect1.width * rect1.height 계산값이 반환된다.getArea 메서드는 프로토타입 메서드다.isRectangle 메서드에 접근하려고 한다.rect1.__proto__(Rectangle.prototype)에서 검색한다.rect1.__proto__.__proto__(Object.prototype)에서 검색한다.isRectangle 메서드는 스태틱 메서드다.위 예제를 통해 프로토타입 메서드와 스태틱 메서드를 확인하였다.
정리하자면
위 예제를 통해 클래스가 추상적일 수도, 구체적일 수도 있다는 것을 알 수 있다.
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
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
클래스 상속을 알아보자.
var Rectangle = class {
constructor(width, height) {
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
};
var Square = class extends Rectangle {
constructor(width) {
super(width, width);
}
getArea() {
console.log('size is: ' + super.getArea());
}
};
var sq = new Square(10);
sq.getArea(); // 'size is: 100'
Square 클래스를 Rectangle 클래스를 상속하는 서브클래스로 만들기 위해 class 뒤에 extends Rectangle을 추가한다. 이것만으로 상속 관계 설정이 끝난다.
super 키워드를 함수처럼 사용할 수 있다.super 키워드를 마치 객체처럼 사용할 수 있다.super 객체는 슈퍼클래스.prototype을 참조한다.super로 호출한 메서드의 this는 super가 아니라 원래의 this를 그대로 따른다.sq.getArea()의 경우 this는 sq가 된다.