자바스크립트의 클래스
자바스크립트는 프로토타입 기반 언어이므로 클래스의 개념이 존재하지 않는다. 그러나 프로토타입은 클래스와 비슷하게 해석할 수 있는 요소들이 있다.
생성자 함수 Array를 new 연산자와 함께 호출하면 인스턴스가 생성된다. 이때 Array를 일종의 클래스라고 하면, Array의 prototype 객체 내부 요소들이 인스턴스에 상속된다고 볼 수 있다. (엄밀히 말하면 상속이 아닌 프로토타입 체이닝에 의한 참조이지만 결과적으로는 동일하게 동작한다.)
또한 자바스크립트에서는 클래스 기반 언어와 달리 인스턴스에 직접 메서드를 정의할 수 있다. 이렇게 직접 정의한 메서드를 '프로토타입 메서드'라고 부른다.
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)); // Error
console.log(Rectangle.isRectangle(rect1)); // true
위 코드를 보면 new 연산자와 함께 Rectangle함수를 호출해서 생성된 인스턴스를 rect1에 할당했다. 이 인스턴스에는 width와 height가 각각 3, 4의 값으로 담겨있다. 프로토타입 객체에 할당한 메서드는 인스턴스가 마치 자신의 것처럼 호출할 수 있으므로 rect1.getArea()가 정상적으로 실행된다. 이렇게 인스턴스에서 직접 호출할 수 있는 메서드가 바로 프로토타입 메서드이다.
반면에 rect1 인스턴스가 isRectangle 메서드에 접근하고자 하면 undefined를 실행하라는 명령어가 되므로 Uncaught TypeError: not a function 에러가 발생한다. 이렇게 인스턴스에서 직접 접근할 수 없는 메서드를 스태틱 메서드라고 한다. 스태틱 메서드는 마지막 줄처럼 생성자 함수를 this로 해야만 호출할 수 있다.
클래스 상속
이번에는 프로토타입 체인을 활용해 클래스 상속을 구현하여 최대한 전통적인 객체지향 언어에서의 클래스와 비슷한 형태로 발전시켜 보자.
var Rectangle = function(width, height) {
this.width = width;
this.height = height;
}
Rectangle.prototype.getArea = function() {
return this.width * this.height;
}
var rect = new Rectangle(3, 4);
console.log(rect.getArea()); // 12
var Square = function(width) {
this.width = width;
this.height = width;
}
Square.prototype.getArea = function() {
return this.width * this.height;
}
var sq = new Square(5);
console.log(sq.getArea()); // 25
각 클래스에 넓이를 구하는 getArea 메서드를 추가했다. getArea 메서드는 동일한 동작을 하므로 상위 클래스에서만 정의하고, 하위 클래스에서는 해당 메서드를 상속하는 방식으로 변경한다.
...
var Square = function(width) {
Rectangle.call(this, width, width)
}
Square.prototype = new Rectangle();
var sq = new Square(5);
console.log(sq.getArea()); // 25
그러나 위 코드만으로 완벽한 클래스 체계가 구축됐다고 할 수 없다.
console.dir(sq) 로 sq인스턴스를 출력하면 클래스에 있는 값이 인스턴스에 영향을 줄 수 있는 구조라는 것을 알 수 있다.
Square의 인스턴스에는 width와 height가 모두 5로 잘 들어가 있다. 그러나 __proto__는 Rectangle의 인스턴스임을 표시하고 있지만 width와 height가 undefined로 할당돼 있음을 볼 수 있다. Square.prototype에 값이 존재하는 것부터가 문제다. 만약 임의로 Square.prototype.width에 값을 부여하고 sq.width의 값을 지워버린다면 프로토타입 체이닝에 의해 엉뚱한 결과가 나오게 된다.
또한, constructor가 여전히 Rectangle을 바라보고 있다는 문제도 있다.
이처럼 클래스에 있는 값이 인스턴스의 동작에 영향을 미쳐서는 안된다. 이런 영향을 줄 수 있다는 사실 자체가 클래스의 추상성을 해치게 된다. 따라서 클래스는 인스턴스와의 관계에서는 구체적인 데이터를 지니지 않고 오직 인스턴스가 사용할 메서드만을 지니는 추상적인 '틀'로서만 작동해야 한다.
클래스가 구체적인 데이터를 지니지 않게 하기
클래스(prototype)가 구체적인 데이터를 지니지 않게 하는 방법은 여러 가지가 있는데, 그 중 가장 쉬운 방법은 만들고 나서 프로퍼티들을 일일이 지우고 더는 새로운 프로퍼티를 추가할 수 없게 하는 것이다.
delete Square.prototype.width;
delete Square.prototype.height;
Object.freeze(Square.prototype);
프로퍼티가 많은 경우를 위해 범용적으로 쓰일 수 있는 아래와 같은 함수를 만들 수 있다.
var extendClass1 = function(SuperClass, SubClass, subMethods) {
SuperClass.prototype = new SuperClass();
for(var prop in SubClass.prototype) {
if(SubClass.prototype.hasOwnProperty(prop)) {
delete SubClass.prototype[prop];
}
}
if(subMethods) {
for(var method in subMethods) {
SubClass.prototype[method] = subMethods[method];
}
}
Object.freeze(SubClass.prototype);
return SubClass;
}
var Square = extendClass1(Rectangle, function(width) {
Rectangle.call(this, width, width);
})
constructor 복구하기
SubClass 인스턴스의 constructor는 여전히 SuperClass를 가리키고 있다. 따라서 위 코드에서 if문 위에 아래 코드를 추가해 SubClass.prototype.constructor가 원래의 SubClass를 바라보도록 한다.
var extendClass1 = function(SuperClass, SubClass, subMethods) {
SuperClass.prototype = new SuperClass();
for(var prop in SubClass.prototype) {
if(SubClass.prototype.hasOwnProperty(prop)) {
delete SubClass.prototype[prop];
}
}
SubClass.prototype.constructor = SubClass; // 추가
if(subMethods) {
for(var method in subMethods) {
SubClass.prototype[method] = subMethods[method];
}
}
Object.freeze(SubClass.prototype);
return SubClass;
}
var Square = extendClass1(Rectangle, function(width) {
Rectangle.call(this, width, width);
})
'개발 > 프론트엔드' 카테고리의 다른 글
[JavaScript] 코어 자바스크립트 week6 - 프로토타입 (0) | 2024.11.26 |
---|---|
[JavaScript] 코어 자바스크립트 week5 - 클로저 (1) | 2024.11.18 |
[JavaScript] 코어 자바스크립트 week4 - 콜백함수 (0) | 2024.11.11 |
[JavaScript] 코어 자바스크립트 week3 -this (0) | 2024.11.05 |
[JavaScript] 코어 자바스크립트 week2 -실행 컨텍스트 (3) | 2024.10.28 |