1. 프로토타입 상속
- 개발에서 기존의 객체가 가지고 있는 기능을 확장해서 비슷한 객체를 만들어야 하는 일이 흔하다. 이 때 사용할 수 있는 것이 프로토타입 상속이다.
* [[Prototype]]
- 자바스크립트 객체는 [[Prototype]] 이라는 숨김 프로퍼티를 갖는다. 이는 null 또는 다른 객체에 대한 참조가 되며, 다른 객체를 참조할 경우 그 대상을 프로토타입이라 부른다.
- 객체에서 프로퍼티를 읽으려고 할 때, 해당 프로퍼티가 없으면 자동으로 프로토타입에서 그 프로퍼티를 찾는데, 이를 '프로토타입 상속'이라 부른다.
- __proto__를 통해 값을 설정할 수 있다. __proto__는 [[Prototype]] 그 자체는 아니며, [[Prototype]]의 getter이자 setter이다. 최근에는 Object.getProtoypeOf와 Object.setPrototypeOf를 써서 프로토타입을 get하거나 set한다.
let animal = {
eats: true,
walk() {
alert("동물이 걷습니다.");
}
};
let rabbit = {
jumps: true,
__proto__: animal
};
let blackRabbit = {
color: "black",
__proto__: rabbit
};
blackRabbit.walk(); // 동물이 걷습니다.
alert(blackRabbit.jumps); // true
- 위에서 rabbit은 프로토 타입으로 animal을 갖고 blackRabbit은 rabbit을 프로토타입으로 갖는 체인 관계이다.
- blackRabiit에서 walk를 호출하면 먼저 rabbit에서 찾으며, 없으므로 rabbit의 프로토타입에서 다시 찾는다.
- blackRabbit에서 jumps를 호출하였고, 프로토 타입인 rabbit에서 바로 찾아서 호출한다.
- 프로토타입 체이닝을 할 때 주의할 점이 있다. 순환참조는 허용되지 않으며, __proto__의 값은 객체나 null만 가능하다.
- for..in 반복문은 상속 프로퍼티도 순회대상에 포함시키는데, obj.hasOwnProperty(key)를 이용하면 상속 프로퍼티를 순회 대상에서 제외 시킬 수 있다.
let animal = {
eats: true
};
let rabbit = {
jumps: true,
__proto__: animal
};
for(let prop in rabbit) {
let isOwn = rabbit.hasOwnProperty(prop);
if (isOwn) {
alert(`객체 자신의 프로퍼티: ${prop}`); // 객체 자신의 프로퍼티: jumps
} else {
alert(`상속 프로퍼티: ${prop}`); // 상속 프로퍼티: eats
}
}
* this
- this는 프로토타입에서 어떻게 동작할까? this는 프로토타입과 관계 없이 .앞에 있는 객체만을 바라본다.
let animal = {
sleep() {
this.isSleeping = true;
}
};
let rabbit = {
name: "하얀 토끼",
__proto__: animal
};
rabbit.sleep();
alert(rabbit.isSleeping); // true
alert(animal.isSleeping); // undefined
- rabbit.sleep()으로 메서드를 시행하였다. 이 때 rabbit에서 메서드를 찾지 못하고 프로토타입인 animal에서 sleep 메서드를 찾는다. this.isSeeping을 true로 바꾸는데 여기서 this는 처음 sleep을 호출한 rabbit을 가리킨다.
2. 함수의 prototype 프로퍼티
* 함수의 prototype
- new F()와 같은 생성자 함수로 새로운 객체를 만들 수 있다. 이 때 F.prototype이 객체라면 new연산자는 F.prototype을 사용해 새롭게 생성된 객체의 [[Prototype]]을 설정한다.
let animal = {
eats: true
};
function Cat(name) {
this.name = name;
}
Cat.prototype = animal;
let cat = new Cat("Black Cat"); // cat.__proto__ == animal
alert( cat.eats ); // true
- 위에서 Cat은 생성자 함수로써 객체를 만들 수 있게 한다.
- Cat.prototype = animal은 new Cat 을 호출해 만든 새 객체의 [[Prototype]]을 animal로 설정하라는 의미를 갖는다.
- 이 때, F.prototype은 new F를 호출할 때만 사용된다. 새 객체가 만들어진 후에 F.prototype 프로퍼티가 바뀌면 기존에 만들어진 객체의 [[Prototype]]은 유지되지만 나중에 new F로 만들어지는 객체는 바뀐 프로퍼티를 [[Prototype]]으로 갖게 된다.
* 함수의 constructor
- 모든 함수는 기본적으로 prototype 프로퍼티를 갖고 있다. 이 때 constructor 프로퍼티 하나만 있는 객체를 가리키며, 이 constructor프로퍼티는 함수 자신을 가리킨다.
function Cat() {}
// Rabbit.prototype = { constructor: Cat };
- 이 때 new Cat으로 구현한 객체는 당연히 .constructor을 호출하면 프로토타입을 거쳐 Cat에 도달한다.
- 그러나 위의 Cat에서 임의적으로 prototype을 변경하면 constructor가 사라진다.
function Cat() {}
Cat.prototype = {
jumps: true
};
let cat = new Cat();
alert(cat.constructor === Cat); // false
- 위를 보면 prototype이 기존 prototype을 덮으면서 constructor가 사라진것을 알 수 있다. 이를 해결하려면 전체를 덮어 쓰지 않고 따로 Cat.prototype.jumps = true와 같이 추가해주거나 prototype내에 constructor: Cat을 명시하여 수동으로 다시 만들어줄 수 있다.
참고
모던 자바스크립트 튜토리얼
'Language > JavaScript' 카테고리의 다른 글
<자바스크립트> try catch & 에러 (0) | 2021.04.16 |
---|---|
<자바스크립트> 클래스의 기본과 상속 (0) | 2021.04.14 |
<자바스크립트> JSON과 메서드 (0) | 2021.04.07 |
<자바스크립트> 구조 분해 할당 (0) | 2021.03.07 |
<자바스크립트> 맵과 셋 (0) | 2021.02.28 |