상속 구현하기: Prototype, Class
상속을 구현하는 방법은 prototype을 이용하기, Class를 이용하기 2가지이다. 사실 Class를 사용한 상속과 prototype을 사용한 상속은 동일하다. prototype의 복잡한 과정을 Class라는 키워드를 통해 간단하게 구현할 수 있다는 점만이 다른 것이다.
1. Prototype
function Animal(name, sound){
this.name = name;
this.sound = sound;
}
Animal.prototype.cry = function(){
console.log(this.sound);
}
const Pigeon = function(name){
//Animal(name, '구구')
//3. this 전달
Animal.call(this, name, '구구')
}
////1. Animal의 prototype을 Bird의 prototype에 대입
Pigeon.prototype = Object.create(Animal.prototype);
////2. constructor 연결
Pigeon.prototype.constructor = Pigeon;
Pigeon.prototype.fly = function() {
console.log('날아가는 중..');
}
let mountainPigeon = new Pigeon('산비둘기');
mountainPigeon.cry(); //'구구'
mountainPigeon.fly(); //'날아가는 중..'
prototype을 이용하여 상속을 진행하기 위해선 3가지 과정이 필요하다.
1) 자식 객체의 prototype을 부모 객체의 prototype으로 변경
상속이라는 것은 부모의 속성들을 자식도 갖고있는 것이다. 따라서 자식인 Pigeon의 prototype을 Animal의 prototype으로 변경한다면 Animal의 name, sound, cry 속성을 Pigeon도 갖게될 것이다. 그 후에 Pigeon 만의 메소드인 fly를 Pigeon.prototype에 추가해준다.
2) constructor 연결
prototype을 변경하면 객체의 prototype을 가리키는 __proto__ 또한 변경되게 된다. 따라서 객체의 프로토타입의 생성자도 변경되게 된다. constructor를 연결하지 않는다면 Pigeon의 생성자는 Animal의 생성자가 된다. 따라서 자식객체 Pigeon의 생성자가 Pigeon의 생성자가 되도록 연결해주어야 한다.
3) this 전달
객체를 만들기 위해 new 키워드를 이용하여 생성자를 호출하면 생성자 내에서 this는 새롭게 생성된 인스턴스 객체를 가리킨다. 그러나 생성자 내에서 다시 부모 객체의 생성자를 호출하면 실행 컨텍스트를 잃어버리고 부모 객체의 생성자내에서 this는 window 객체를 가리키게 된다. 이 경우 새로 생성된 객체인 mountainPigeon의 name과 sound가 undefined가 된다.(window.sound가 '구구'가 되버린다) 따라서 call 또는 apply를 사용하여 부모 객체의 생성자에게 this를 전달해주어야 한다.
2. Class
class Animal{
constructor(name, sound){
this.name = name;
this.sound = sound;
}
cry(){
console.log(this.sound);
}
}
class Pigeon extends Animal{
constructor(name){
super(name, '구구'); //부모생성자 호출
}
fly(){
console.log("날아가는중");
}
}
위의 코드는 Prototype에서 상속을 구현한 것과 동일하게 동작한다. extends 부분에서 prototype과 constructor가 지정되고, super를 통해 this가 전달된다. 만약 부모 객체의 constructor와 자식 객체의 constructor가 동일하다면 constructor부분을 생략할 수 있다.