자바스크립트는 프로토타입 기반 언어라 상속이 존재 하지 않음.
클래스와 비슷한 여러 기법이 나타 났고 ES6에 클래스 문법이 추가 되었음.
But, ES6의 클래스에서도 일정 부분은 프로토타입을 활용하고 있기 때문에 ES5 체제에서 클래스 구현방식을 학습해야함
상위클래스 - superclass
하위클래스 - subclass
프로그래밍 언어에서의 클래스는 현실세계의 클래스와 마찬가지로 '공통 요소를 지니는 집단을 분류하기 위한 개념' 이라는 측면에서는 일치하지만, 클래스가 먼저 정의돼야만 그로부터 공통적인 요소를 지니는 개체들을 생성할 수 있다는 차이가 있음. 프로그래밍 언어에서의 클래스는 사용하기에 따라 추상적인 대상일 수도 있고 구체적인 개체가 될 수도 있음
상속
된다고 볼 수 있음상속
되지 않음프로토타입 메서드
라고 부르는게 좋음// 생성자 함수
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
// 잘못된 호출: rect1은 스태틱 메서드를 호출할 수 없음
console.log(rect1.isRectangle(rect1)); // TypeError
// 올바른 호출: 스태틱 메서드는 클래스 이름을 통해 호출해야 함
console.log(Rectangle.isRectangle(rect1)); // true
// 생성자 함수
var Rectangle = function (width, height) {
this.width = width;
this.height = height;
};
// 프로토타입 메서드 정의
Rectangle.prototype.getArea = function () {
return this.width * this.height;
};
// 인스턴스 생성
var rect1 = new Rectangle(3, 4);
// 인스턴스의 구체적인 데이터 제거
delete rect1.width;
delete rect1.height;
// 새로운 프로퍼티 추가 방지
Object.freeze(rect1);
console.log(rect1.getArea()); // NaN (width와 height가 없기 때문)
// 빈 생성자 함수
var Rectangle = function () {};
// 프로토타입 메서드 정의
Rectangle.prototype.init = function (width, height) {
this.width = width;
this.height = height;
};
Rectangle.prototype.getArea = function () {
return this.width * this.height;
};
// 인스턴스 생성 및 초기화
var rect1 = new Rectangle();
rect1.init(3, 4);
console.log(rect1.getArea()); // 출력: 12
// 상위 클래스 정의
var Shape = {
init: function (type) {
this.type = type;
},
getType: function () {
return this.type;
},
};
// 하위 클래스 생성
var Rectangle = Object.create(Shape);
Rectangle.init = function (width, height) {
Shape.init.call(this, "Rectangle");
this.width = width;
this.height = height;
};
Rectangle.getArea = function () {
return this.width * this.height;
};
// 인스턴스 생성
var rect1 = Object.create(Rectangle);
rect1.init(3, 4);
console.log(rect1.getType()); // 출력: "Rectangle"
console.log(rect1.getArea()); // 출력: 12
// SuperClass 정의
var Rectangle = function (width, height) {
this.width = width;
this.height = height;
};
Rectangle.prototype.getArea = function () {
return this.width * this.height;
};
// SubClass 정의
var Square = function (width) {
Rectangle.call(this, width, width); // SuperClass 생성자 호출
};
// 프로토타입 상속 및 constructor 수정
Square.prototype = Object.create(Rectangle.prototype);
Square.prototype.constructor = Square; // constructor 설정
Object.freeze(Square.prototype); // 프로토타입 동결
// 인스턴스 생성 및 사용
var sq = new Square(5);
console.log(sq.getArea()); // 출력: 25
console.log(sq.constructor === Square); // 출력: true (constructor가 올바르게 설정됨)
var extendClass2 = (function () {
var Bridge = function () {};
return function (SuperClass, SubClass, subMethods) {
Bridge.prototype = SuperClass.prototype;
SubClass.prototype = new Bridge();
SubClass.prototype.constructor = SubClass; // constructor 설정
// SubClass의 메서드 추가
if (subMethods) {
for (var method in subMethods) {
if (subMethods.hasOwnProperty(method)) {
SubClass.prototype[method] = subMethods[method];
}
}
}
Object.freeze(SubClass.prototype); // 프로토타입 동결
return SubClass;
};
})();
// SuperClass 정의
var Rectangle = function (width, height) {
this.width = width;
this.height = height;
};
Rectangle.prototype.getArea = function () {
return this.width * this.height;
};
// SubClass 정의 및 상속
var Square = function (width) {
Rectangle.call(this, width, width);
};
extendClass2(Rectangle, Square);
// 인스턴스 생성 및 사용
var sq = new Square(5);
console.log(sq.getArea()); // 25
console.log(sq.constructor === Square); // true
var extendClass3 = function (SuperClass, SubClass, subMethods) {
// SuperClass의 프로토타입을 SubClass의 프로토타입으로 설정
SubClass.prototype = Object.create(SuperClass.prototype);
// SubClass의 constructor를 올바르게 설정
SubClass.prototype.constructor = SubClass;
// SubClass의 메서드 추가
if (subMethods) {
for (var method in subMethods) {
if (subMethods.hasOwnProperty(method)) {
SubClass.prototype[method] = subMethods[method];
}
}
}
Object.freeze(SubClass.prototype); // 프로토타입 동결
return SubClass;
};
// SuperClass 정의
var Rectangle = function (width, height) {
this.width = width;
this.height = height;
};
Rectangle.prototype.getArea = function () {
return this.width * this.height;
};
// SubClass 정의 및 상속
var Square = function (width) {
Rectangle.call(this, width, width);
};
extendClass3(Rectangle, Square);
// 인스턴스 생성 및 사용
var sq = new Square(5);
console.log(sq.getArea()); // 25
console.log(sq.constructor === Square); // 출력: true
// 상위 클래스와 하위 클래스를 확장하는 함수 정의
var extendClass = function (SuperClass, SubClass, subMethods) {
// SuperClass의 프로토타입을 SubClass의 프로토타입으로 설정
SubClass.prototype = Object.create(SuperClass.prototype);
// SubClass의 constructor를 올바르게 설정
SubClass.prototype.constructor = SubClass;
// super 메서드 정의
SubClass.prototype.super = function (propName) {
var self = this;
if (!propName) {
// 인자가 비어있을 경우 상위 클래스의 생성자 함수 호출
return function () {
SuperClass.apply(self, arguments);
};
}
var prop = SuperClass.prototype[propName];
if (typeof prop !== "function") {
// propName이 함수가 아닌 경우 해당 값을 그대로 반환
return prop;
}
// propName이 함수인 경우 클로저를 활용하여 메서드에 접근
return function () {
return prop.apply(self, arguments);
};
};
// 하위 클래스의 추가 메서드 정의
if (subMethods) {
for (var method in subMethods) {
if (subMethods.hasOwnProperty(method)) {
SubClass.prototype[method] = subMethods[method];
}
}
}
// SubClass의 프로토타입을 동결
Object.freeze(SubClass.prototype);
return SubClass;
};
// SuperClass 정의
var Rectangle = function (width, height) {
this.width = width;
this.height = height;
};
// SuperClass의 프로토타입 메서드 정의
Rectangle.prototype.getArea = function () {
return this.width * this.height;
};
// 하위 클래스 정의 및 상속
var Square = extendClass(
Rectangle,
function (width) {
// super 사용하여 상위 클래스의 생성자 호출
this.super()(width, width);
},
{
// 하위 클래스의 메서드 정의
getArea: function () {
// super 사용하여 상위 클래스의 메서드 호출
console.log("size is :", this.super("getArea")());
},
}
);
// 인스턴스 생성 및 사용
var sq = new Square(10);
sq.getArea(); // size is : 100
console.log(sq.super("getArea")()); // 100
// ES5 클래스 정의
var ES5 = function (name) {
this.name = name;
};
// 정적 메서드 추가
ES5.staticMethod = function () {
return this.name + " staticMethod";
};
// 프로토타입 메서드 추가
ES5.prototype.method = function () {
return this.name + " method";
};
// ES5 인스턴스 생성과 메서드 호출
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";
}
};
// ES6 인스턴스 생성과 메서드 호출
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;
}
};
// Square 클래스가 Rectangle 클래스를 상속받음
var Square = class extends Rectangle {
constructor(width) {
// 상위 클래스의 생성자를 호출하여 width, height를 초기화
super(width, width);
}
// 인스턴스 메서드 재정의 (오버라이딩)
getArea() {
// 상위 클래스의 getArea 메서드를 호출
console.log("size is:", super.getArea());
}
};
// 인스턴스 생성과 메서드 호출
var squareInstance = new Square(5);
squareInstance.getArea(); // 출력: size is: 25
ES5, ES6 차이점
클래스 정의
ES5: 생성자 함수와 프로토타입을 사용하여 클래스를 정의
ES6: class 키워드를 사용하여 클래스를 정의
정적 메서드
ES5: 정적 메서드는 생성자 함수에 직접 추가
ES6: static 키워드를 사용하여 클래스 내부에서 정의
상속
ES5: 프로토타입 체인을 통해 상속을 구현
ES6: extends와 super 키워드를 사용하여 쉽게 상속을 구현
가독성
ES5: 코드가 다소 복잡하고 장황함
ES6: 코드가 간결하고 가독성이 높음
자바스크립트는 프로토타입 기반 언어라서 클래스 및 클래스의 상속 개념은 없지만 클래스 기반 언어와 비슷하게 동작하게끔 하는 다양한 기법들이 도입 되어 옴
클래스는 어떤 사물의 공통 속성을 모아 정의한 추상적 개념이고, 인스턴스는 클래스의 속성을 지니는 구체적인 사례
상위 클래스(superclass)의 조건을 충족하면서 더 구체적인 조건이 추가된 것을 하위 클래스(subclass)
클래스의 prototype 내부에 정의된 메서드를 프로토타입 메서드라고 하며 이 메서드들은 인스턴스에서 자신의 것처럼 호출할 수 있음
클래스(생성자 함수)에 직접 정의한 메서드를 스태틱 메서드라고 하며 이 메서드들은 인스턴스가 직접 호출할 수 없고 클래스(생성자 함수)에 의해서만 호출됨
클래스 상속을 흉내내기 위한 3가지 방법