자바스크립트의 객체는 [[prototype]]이라는 숨김 프로퍼티를 가진다
이 숨김 프로퍼티 값은 null이거나 다른 객체에 대한 참조가 되는데, 다른 객체를 참조하는 경우 참조 대상을 '프로토타입(prototype)'이라 부른다
object에서 프로퍼티를 읽으려 할 때 해당 프로퍼티가 없으면 자동으로 프로토타입에서 프로퍼티를 찾는 동작 방식
[[Prototype]] 프로퍼티는 내부 프로퍼티이면서 숨김 프로퍼티이지만 다양한 방법을 사용해 개발자가 값을 설정할 수 있다
let animal = {
eats: true
};
let rabbit = {
jumps: true
};
rabbit.__proto__ = animal;
👉[[prototype]]의 getter이자 setter
__proto__ != [[Prototype]]
하위 호환성 때문에 여전히 __proto__를 사용할 순 있지만 비교적 근래에 작성된 스크립트에서는 __proto__대신 Object.getPrototypeOf나 Object.setPrototypeOf를 써서 프로토타입을 get하거나 set한다
__proto__는 브라우저 환경에서만 지원하도록 자바스크립트 명세서에 규정되었으나 실상은 서버 사이드를 포함한 모든 호스트 환경에서 __proto__를 지원한다
let animal = {
eats: true
};
let rabbit = {
jumps: true
};
rabbit.__proto__ = animal; // (1)
alert(rabbit.eats); // true, (2)
alert(rabbit.jumps); // true
(1) > animal이 rabbit의 프로토타입이 되도록 설정
(2) > alert함수가 rabbit.eats 프로퍼티를 읽으려 했으나 rabbit엔 eats라는 프로퍼티가 없다.
이 때 [[Prototype]]이 참조하는 객체인 animal에서 eats를 얻어낸다
👉 rabbit의 프로토타입은 animal이다
👉 rabbit은 animal을 상속 받는다
이렇게 프로토타입에서 상속받은 프로퍼티를 상속 프로퍼티(inherited property)라고 한다
let animal = {
eats: true,
walk() { alert('동물이 걸어요'); }
};
let rabbit = {
jumps: true,
__proto__: animal
};
// rabbit의 프로토타입인 animal에서 상속받은 메서드 walk
rabbit.walk(); // 동물이 걸어요
let animal = {
eats: true,
walk() {
alert('동물이 걸어요');
}
};
let rabbit = {
jumps: true,
__proto__: animal
};
let longEar = {
earLength: 10,
__proto__: rabbit
};
// 프로토타입 체인을 통해 상속받은 메서드 walk
longEar.walk(); // 동물이 걸어요
alert(longEar.jumps); // true (rabbit에서 상속받음)
⚠️ 프로토타입 체이닝의 제약사항
__proto__를 이용해 닫힌 형태로 다른 객체를 참조하면 에러가 발생__proto__의 값은 객체나 null만 가능하고 다른 자료형은 무시된다[[Prototype]]만 있을 수 있다 = 객체는 두 개의 객체를 상속받지 못한다프로퍼티를 추가, 수정하거나 지우는 연산은 객체에 직접 해야한다
let animal = {
eats: true,
walk() {} // rabbit은 이제 이 메서드를 사용하지 않음
};
let rabbit = {
__proto__: animal
};
rabbit.walk = function() {
alert('토끼가 뛰어요');
};
rabbit.walk(); // 토끼가 뛰어요
위 처럼 객체 rabbit에 walk메서드를 직접 할당한 후 rabbit.walk()를 호출하면 프로토타입의 메서드가 실행되지 않고 객체 rabbit에 추가한 메서드가 실행된다
let user = {
name: 'Jinju',
surname: 'Baek',
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); // Jinju Baek (1)
// setter 함수가 실행되는 부분
admin.fullName = 'John Doe'; // (2)
alert(admin.fullName); // John doe
alert(user.fullName); // Jinju Baek
(1) > admin.fullName은 프로토타입 user의 getter 함수를 호출
(2) > 프로토타입의 setter 함수를 호출
setter 함수의 this에 어떤 값이 들어갈까?
프로퍼티 this.name과 this.surname에 값을 쓰면 그 값은 user에 저장될까 아니면 admin에 저장될까?
👉 this는 프로토타입에 영향을 받지 않는다
메서드를 객체에서 호출했든 프로토타입에서 호출했든 상관없이 this는 . 앞에 있는 객체가 된다.
admin.fullName으로 setter 함수를 호출하면 this는 user가 아닌 admin이 된다
for..in은 상속 프로퍼티도 순회대상에 포함시킨다
let animal = {
eats: true
};
let rabbit = {
jumps: true,
__proto__ : animal
};
// Object.keys는 객체 자신의 key만 반환한다
alert(Object.keys(rabbit)); // jumps
// for..in은 객체 자신의 key와 상속 프로퍼티 key 모두를 순회
for(let prop in rabbit) alert(prop); // jumps, eats
❗key-value를 순회하는 메서드 대부분은 상속 프로퍼티를 제외하고 동작한다
Object.keys, Object.values같이 객체의 key-value를 대상으로 무언가를 하는 메서드 대부분은 상속 프로퍼티를 제외하고 동작한다
프로토타입에서 상속받은 프로퍼티는 제외하고, 해당 객체에서 정의한 프로퍼티만 연산 대상에 포함한다
[[Prototype]]이 있는데, 이 프로퍼티는 객체나 null을 가리킨다obj.__proto__를 사용하면 프로토타입에 접근할 수 있다__proto__는 [[Prototype]]의 getter와 setter로 쓰이는데 요즘엔 잘 쓰이지 않는다obj에서 프로퍼티를 읽거나 메서드를 호출하려는데 해당하는 프로퍼티나 메서드가 없으면 자바스크립트는 프로토타입에서 프로퍼티나 메서드를 찾는다method라도 obj.method()를 호출하면 method안의 this는 호출 대상 객체인 obj를 가리킨다for..in 반복문은 객체 자체에서 정의한 프로퍼티뿐만 아니라 상속 프로퍼티도 순회 대상에 포함한다