이전 이야기가 뭐였나면..
이제 프로토타입 공정 도입으로 더 효율적으로 객체를 생산하게 되었습니다.
우리가 생산하는 객체는 바로 마늘 소시지입니다.
그런데, 이제 똑같은 소시지만 생산하다보니 예전보다 덜 팔리게 되어서
불맛나는 소시지를 만들고 싶습니다. 어떻게 하면 될까요?
코드 예시
- 마늘맛 소시지
function Sausage(el1, el2) {
this.inside1 = el1;
this.inside2 = el2;
}
Sausage.prototype.taste = function() {
return this.inside1 + "와 "+ this.inside2 + " 맛이 난다!";
}
var mySausage = new Sausage("돼지고기", "마늘");
console.log(mySausage.taste()); // "돼지고기와 마늘 맛이 난다!"
- 불맛나는 소시지
function FireSausage(el1, el2, el3) {
this.inside1 = el1;
this.inside2 = el2;
this.inside3 = el3;
}
FireSausage.prototype.taste = function() {
return this.inside1 + "와 "+ this.inside2 + " 맛이 난다!";
}
FireSausage.prototype.flavor = function() {
return this.inside3 + "의 풍미도 있다!";
}
var myNewSauage = new Sausage("돼지고기", "마늘", "불맛");
console.log(myNewSauage.taste()); // "돼지고기와 마늘 맛이 난다!"
console.log(myNewSauage.flavor()); // "불맛의 풍미도 있다!"
FireSausage 타입을 새로 만들었지만,
소시지와 불맛 소시지의 제작공정이 너무 겹쳐서 불만족스럽습니다.
this.inside3 = el3와 flavor()만 추가된 것일 뿐입니다.
이를 효율적으로 관리할 방법은 없을까요?
1. 생성자 훔치기
코드 예시
function Sausage(el1, el2) {
this.inside1 = el1;
this.inside2 = el2;
}
Sausage.prototype.taste = function() {
return this.inside1 + "와 "+ this.inside2 + " 맛이 난다!";
}
var mySausage = new Sausage("돼지고기", "마늘");
function FireSausage(el1, el2, el3) {
Sauage.call(this, el1, el2);
this.inside3 = el3;
}
var myNewSauage = new FireSausage("돼지고기", "마늘", "불맛");
console.log(myNewSauage.el1); // "돼지고기"
console.log(myNewSauage.el2); // "마늘"
console.log(myNewSauage.el3); // "불맛"
생성자 함수이기 때문에 call() 메서드를 사용할 수 있습니다.
FireSausage 생성자 함수의 this는 FireSausage의 인스턴스입니다.
그렇기 때문에 Sauage.call의 this는 바로 FireSausage의 인스턴스를 가리키게 됩니다.
이렇게 call이나 apply를 이용하여 인스턴스를 인수로 전달하고
프로퍼티를 상속받는 방법을 "생성자 훔치기(constructor stealing)"이라고 표현합니다.
하위 타입(subtype)과 상위 타입(supertype)
이처럼 프로퍼티를 상속받는 타입을 하위 타입(subtype),
상속을 해주는 타입을 상위 타입(supertype) 이라고 합니다.
2. 프로토타입 상속
생성자 훔치기를 통해 프로퍼티만 상속받을 수 있어요.
하지만 메서드는 상속 받지 못한답니다.
따라서 Sauage.prototype.taste()는 FireSauage에서 사용할 수 없어요.
console.log(myNewSausage.taste());
// ❌ TypeError: myNewSausage.taste is not a function
따라서 prototype 상속도 필요합니다.
그럼 Sausage 타입의 프로토타입(taste)도 가져다 써봅시다.
FireSausage.prototype = Object.create(Sauage.prototype);
FireSausage.prototype.constructor = FireSausage;
FireSausage.prototype.flavor = function() {
return this.inside3 + "의 풍미도 있다!";
}
❓ Object.create() 메서드
Object.create() 메서드는 [[Prototype]]이 참조할 생성자의 prototype 프로퍼티를 설정합니다.
즉, 프로퍼티가 어느 생성자를 가리킬지 정할 수 있다는 겁니다.
this를 가리키는 것을 정할 수 있는 call과 특성이 비슷하죠.
Object.create(Sausage.prototype);
그러므로 이렇게 작성하게 된다면,
"이 객체는 Sauage의 기능도 쓸 수 있어요!"라고 선언하는 것과 동일합니다.
말하자면, 부모님 레시피(Sauage)를 그대로 물려받은 불맛 소시지(FireSauage) 느낌이에요.
❓ constructor 복구
이제 프로토타입 체인을 통해서 상위 타입의 메서드도 사용할 수 있게 되었습니다.
이때, 인스턴스의 constructor를 FireSausage로 설정해야한다는 것에 주의해야 합니다.
FireSausage.prototype.constructor = FireSausage;
consturctor는 "이 객체를 만든 설계도" 같은 역할을 합니다.
생략하면 Sauage로 잘못 표기되기 때문에 꼭 다시 설정해줘야 합니다.
해당 코드가 없으면, FireNewSauage의 prototype의 constructor 프로퍼티가 없습니다.
지금은 코드가 별로 줄지 않은 것 같지만,
나중에 상위 타입의 기능이 많아질수록 상속은 훨씬 더 효율적인 방법이 됩니다!
✨ Recap
- call or apply를 이용하여 인스턴스를 인수로 전달하고
프로퍼티를 상속받는 방법을 "생성자 훔치기"라고 합니다. - Object.create() 메서드를 통해 인스턴스의 [[Prototype]] 대상을 지정할 수 있습니다.
- 자바스크립트에서는 상속받는 타입을 하위 타입(subtype),
상속하는 타입을 상위 타입(supertype)이라고 부릅니다.
'Front-End: Web > JavaScript' 카테고리의 다른 글
<호이스팅>이 뭘까? 함수/변수 선언이 끌어올려지는 이유 (0) | 2025.07.13 |
---|---|
ES6 클래스와 상속 쉽게 이해하기 (1) | 2025.07.07 |
생성자 함수의 진화: 프로토타입으로 효율적인 객체 생성하기 (0) | 2025.07.06 |
'new'만 붙이면 끝? 생성자 함수부터 클래스까지 제대로 이해하기 (2) | 2025.07.06 |
외부 함수가 사라져도 변수가 살아있는 이유: <클로저(Closure)> (0) | 2025.07.05 |