프로토타입 상속
- 자바스크립트의 객체는 명세서에서 명명한
[[Prototype]]
이라는 숨김 프로퍼티가 존재. 이 숨김 프로퍼티 값은 null
이거나 다른 객체에 대한 참조가되는데, 다른 객체를 참조하는 경우 참조 대상을 '프로토타입(prototype)'이라 함
oject
에서 프로퍼티를 읽으려고 하는데 해당 프로퍼티가 없으면 자바스크립트는 자동으로 프로토타입에서 프로퍼티를 탐색. 이러한 동작 방식을 '프로토타입 상속'이라 함
let animal = {
eats: true
walk() {
alert("동물이 걷습니다.");
}
};
let rabbit = {
jumps: true
};
rabbit.__proto__ = animal;
alert( rabbit.jumps );
alert( rabbit.eats );
rabbit.walk();
__proto__
는 [[Prototype]]
의 getter(획득자)이자 setter(설정자)
- 객체
rabbit
에서 프로퍼티를 얻고싶은데 해당 프로퍼티가 없다면, 자바스크립트는 자동으로 animal
이라는 객체에서 프로퍼티를 탐색
rabbit
의 프로토타입은 animal
/ rabbit
은 animal
을 상속 / eats
는 "상속 프로퍼티(inherited property)"
let animal = {
eats: true,
walk() {
}
};
let rabbit = {
__proto__: animal
};
rabbit.walk = function() {
alert("토끼가 깡충깡충 뜁니다.");
};
rabbit.walk();
- 프로토타입은 프로퍼티를 읽을 때만 사용
- 프로퍼티를 추가, 수정하거나 지우는 연산은 객체에 직접
rabbit.walk()
를 호출하면 프로토타입에 있는 메서드가 실행되지 않고, 객체 rabbit
에 추가한 메서드가 실행
- 접근자 프로퍼티(accessor property)는 setter 함수를 통해서 프로퍼티에 값을 할당하므로 이 규칙이 적용되지 않음. 접근자 프로퍼티에 값을 할당하는 것은 함수를 호출하는 것과 같기 때문
let user = {
name: "John",
surname: "Smith",
set fullName(value) {
[this.name, this.surname] = value.split(" ");
},
get fullName() {
return `${this.name} ${this.surname}`;
}
};
let admin = {
__proto__: user,
isAdmin: true
};
alert(admin.fullName);
admin.fullName = "Alice Cooper";
alert(admin.fullName);
alert(user.fullName);
(*)
로 표시한 줄에서 admin.fullName
은 프로토타입(user
)에 있는 getter 함수(get fullName
)를 호출하고, (**)
로 표시한 줄의 할당 연산은 프로토타입에 있는 setter 함수(set fullName
)를 호출
let animal = {
walk() {
if (!this.isSleeping) {
alert(`동물이 걸어갑니다.`);
}
},
sleep() {
this.isSleeping = true;
}
};
let rabbit = {
name: "하얀 토끼",
__proto__: animal
};
rabbit.sleep();
alert(rabbit.isSleeping);
alert(animal.isSleeping);
this
는 프로토타입에 영향을 받지 않음
- 메서드를 객체에서 호출했든 프로토타입에서 호출했든 상관없이
this
는 언제나 .
앞에 있는 객체
this
에 데이터를 쓰면 animal
이 아닌 해당 객체의 상태가 변화
- 메서드는 공유되지만, 객체의 상태는 공유되지 않는다고 결론
프로토타입 체이닝
let animal = {
eats: true,
walk() {
alert("동물이 걷습니다.");
}
};
let rabbit = {
jumps: true,
__proto__: animal
};
let longEar = {
earLength: 10,
__proto__: rabbit
};
longEar.walk();
alert(longEar.jumps);
- 순환 참조(circular reference)는 비허용.
__proto__
를 이용해 닫힌 형태로 다른 객체를 참조하면 에러가 발생
__proto__
의 값은 객체나 null
만 가능. 다른 자료형은 무시
- 객체엔 오직 하나의
[[Portotype]]
만 존재. 객체는 두 개의 객체를 상속받지 못함