프로토타입 체인
자바스크립트는 객체의 프로퍼티에 접근하려 할 때 해당 객체에 접근하려는 프로퍼티가 없다면 [[Prototype]] 내부 슬롯의 참조를 따라 자신의 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색함. 이를 프로토타입 체인이라 함
프로토타입 체인은 자바스크립트가 객체지향 프로그래밍의 상속을 구현하는 메커니즘
function Person(name) {
this.name = name;
}
//프로토타입 메서드
Person.prototype.sayHello = function () {
console.log(`Hi! My name is ${this.name}`);
};
const me = new Person("Lee");
//hasOwnProperty는 Object.prototype의 메서드
console.log(me.hasOwnProperty("name")); //true
//getPrototypeOf도 마찬가지
Object.getPrototypeOf(Person.prototype) === Object.prototype;
console.log(Object.getPrototypeOf(Person.prototype) === Object.prototype); //true
ex)me.hasOwnProperty("name")의 메서드 검색 순서
1. hasOwnProperty 메서드를 호출한 me객체에서 해당 메서드를 검색, me객체에는 hasOwnProperty 메서드가 없으므로 프로토타입을 체인을 따라, [[Prototype]] 내부 슬롯에 바인딩되어 있는 프로토타입(Person.prototype)으로 이동
2. Person.prototype 에더 hasOwnPropertyt 메서드가 없으므로 프로토타입 체인을 따라 Obejct.prototype으로 이동
3. Object.prototype에는 hasOwnProperty가 존재함. 자바엔진은 Object.prototype.hasOwnProperty 메서드를 호출함. 이때 해당 메서드의 this에는 me 객체가 바인딩됨.
console.log(Object.prototype.hasOwnProperty.call(me, "name")); //true
Object.prototype을 프로토타입 체인의 종점 이라고 함.
Object.prototype의 프로토타입의 값은 null
프로토타입 체인은 상속과 프로퍼티 검색을 위한 메커니즘
스코프 체인은 식별자 검색을 위한 메커니즘
me.hasOwnProperty('name')의 경우 스코프 체인이 실행에서 me식별자를 먼저 검색한 후, 프로토타입 체인에서 hasOwnProperty 메서드를 검색함
오버라이딩과 프로퍼티 섀도잉
const Person = (function () {
//생성자 함수
function Person(name) {
this.name = name;
}
//프로토타입 메서드
Person.prototype.sayHello = function () {
console.log(`Hi! 난 ${this.name}`);
};
//생성자 함수를 반환
return Person;
})();
const me = new Person("Lee");
//인스턴스 메서드
me.sayHello = function () {
console.log(`Hey! so dlfma ${this.name}`);
};
// 인스턴스 메서드가 호출됨. 프로토타입 메서드는 인스턴스 메서드에 의해 가려짐
me.sayHello(); // Hey! so dlfma Lee
프로토타입 프로퍼티와 같은 이름의 프로퍼티를 인스턴스에 추가하면 인스턴스 프로퍼티로 추가한다. 이때 인스턴스 메서드 sayHello는 프로토타입 메서드 sayHello를 오버라이딩 했고 프로토타입 메서드는 가려진다. 이처럼 상속관계에 의해 프로퍼티가 가려지는 현상을 프로퍼티 섀도잉
오버라이딩 : 상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정의하여 사용하는 방식
오버로딩 : 함수 이름은 동일하지만 매개변수의 타입 또는 개수가 다른 메서드를 구현하고 매개변수에 의해 메서드를 구별하여 호출하는 방식 ( 자바 스크립트는 오버로딩을지원하지 않지만 arguments 객체를 사용하여 구현 가능)
delete me.sayHello;
me.sayHello(); // Hi! 난 Lee
하위 객체를 통해 프로토타입의 프로퍼티를 변경 또는 삭체는 불가능 (get 액세스는 허용되나 set 액세스는 허용 x)
삭제하려면 프로토타입에 직접 접근해야함. (delete Person.prototype.sayHello)
프로토타입의 교체
프로토타입은 임의의 다른 객체로 변경할 수 있음. 부모 객체인 프로토타입을 동적으로 변경 가능
이를 통해 객체간 상속관계를 동적 변경 가능
1. 생성자 함수에 의한 프로토타입의 교체
프로토타입 교체
const Person = (function () {
//생성자 함수
function Person(name) {
this.name = name;
}
//생성ㅈ 함수의 prototype 프로퍼티를 통해 프로토타입을 교체
Person.prototype = {
sayHello() {
console.log(`Hi! 난 ${this.name}`);
},
};
//생성자 함수를 반환
return Person;
})();
const me = new Person("Lee");
// 프로토타입을 교체하면 constructor 프로퍼티와 생성자 함수 간의 연결이 파괴됨
console.log(me.constructor === Person); //false
// 프로토타입 체인을 따라 Object.prototype의 constructor 프로퍼티가 검색됨
console.log(me.constructor === Object); //true
constructor 프로퍼티와 생성자 함수간의 연결이 파괴딤
프로토타입으로 교체한 객체 리터럴에 constructor 프로퍼티를 추가하여 프로토타입의 constructor 프로퍼티를 되살림
const Person = (function () {
//생성자 함수
function Person(name) {
this.name = name;
}
//생성자 함수의 prototype 프로퍼티를 통해 프로토타입을 교체
Person.prototype = {
//constructor 프로퍼티와 생성자 함수간의 연결을 설정
constructor: Person,
sayHello() {
console.log(`Hi! 난 ${this.name}`);
},
};
//생성자 함수를 반환
return Person;
})();
const me = new Person("Lee");
// 프로토타입을 교체하면 constructor 프로퍼티와 생성자 함수 간의 연결이 파괴됨
console.log(me.constructor === Person); //true
// 프로토타입 체인을 따라 Object.prototype의 constructor 프로퍼티가 검색됨
console.log(me.constructor === Object); //false
2. 인스턴스에 의한 프로토타입의 교체
프로토타입은 생성자 함수의 prototype 프로퍼티 뿐만 아니라 인스턴스의 __proto__ 접근자 프로퍼티를 통해 접근 및 교체가능
혹은 Object.getPrototypeOf 메서드 와 Object.setPrototypeOf 메서드를 통해 접근 및 교체 가능
function Person(name) {
this.name = name;
}
const me = new Person("Lee");
//프로토타입으로 교체할 객체
const parent = {
sayHello() {
console.log(`Hi! My name is ${this.name}`);
},
};
//1. me 객체의 프로토타입을 parent 객체로 교체
Object.setPrototypeOf(me, parent);
//위 코드는 아래의 코드와 동이랗게 작동
//me.__proto__ = parent
me.sayHello(); //Hi! My name is Lee
// 프로토타입을 교체하면 constructor 프로퍼티와 생성자 함수 간의 연결이 파괴됨
console.log(me.constructor === Person); //false
// 프로토타입 체인을 따라 Object.prototype의 constructor 프로퍼티가 검색됨
console.log(me.constructor === Object); //true
생성자 함수와 프로토타입간의 연결 되살리기
function Person(name) {
this.name = name;
}
const me = new Person("Lee");
//프로토타입으로 교체할 객체
const parent = {
//constructor 프로퍼티와 생성자 함수간의 연결을 상징
constructor: Person,
sayHello() {
console.log(`Hi! My name is ${this.name}`);
},
};
//생성자 함수의 prototype 프로퍼티와 프로토타입 간의 연결을 설정
Person.prototype = parent;
//me 객체의 프로토타입을 parent 객체로 교체
Object.setPrototypeOf(me, parent);
//위 코드는 아래의 코드와 동일하게 작동
//me.__proto__ = parent
me.sayHello(); //Hi! My name is Lee
// constructor 프로퍼티와 생성자 함수를 가리킴
console.log(me.constructor === Person); //true
console.log(me.constructor === Object); //false
console.log(Person.prototype === Object.getPrototypeOf(me)); //true
instanceof 연산자
instanceof 연산자는 이항 연산자로서좌벼넹 객체를 가리키는 식별자, 우변에 생성자 함수를 가리키는 식별자를 피연산자로 받음
객체 instanceof 생성자 함수
우변의 생성자 함수의 prototype에 바인딩된 객체가 좌변의 객체의 프로토타입 체인 상에 존재하면 true로 평가되고, 그렇지 않은 경우에는 false로 평가 됨
instanceof 연산자는 프로토타입의 constructor 프로퍼티가 가리키는 생성자 함수를 찾는 것이 아니라 생성자 함수의 prototype에 바인딩된 객체가 프로토타입 체인상에 존재하는지 확인함.
(생성자 함수에 의해 프로토타입이 교체되어 constructor 프로퍼티와 생성자 함수 간의 연결이 파괴되어도 생성자 함수의 prototype 프로퍼티와 프로토타입 간의 연결은 파괴되지 않으므로 instanceof는 아무런 영향을 받지 않음.
직접 상속
1. Object.create에 의한 직접 상속
Object.create 메서드는 명시적으로 프로토타입을 지정하여 새로운 객체를 생성함. Object.create 메서드도 다른 객체 생성 방식과 마찬가지로 추상 연산 OrdinaryObjectCreate를 호출함.
첫 번재 매개변수에는 새엇ㅇ할 객체의 프로토타입으로 지정할 객체를 전달함
두 번째 맥변수에는 생성할 객체의 프로퍼티 키와 프로퍼티 디스크립터 객체로 이뤄진 객체를 전달함.
이 객체의 형식은 Object.defineProperties 메서드의 두 번째 인수와 동일함.(생략 가능)
/**
*지정된 프로토타입 및 프로퍼티를 갖는 새로운 객체를 생성하여 반환함
*param {Object} prototype - 생성할 객체의 프로토타입으로 지정할 객체
*param {Object} [propertiesObject] - 생성할 객체의 프로퍼티를 갖는 객체
*returns {Object} 지정된 프로토타입 및 프로퍼티를 갖는 새로운 객체
*/
Object.create(prototype[, propertiesObject])
'강의 노트 > JS' 카테고리의 다른 글
| [JS deep dive] 빌트인 객체 (0) | 2023.04.05 |
|---|---|
| [JS deep dive] strict mode (0) | 2023.04.03 |
| [JS deep dive] 프로토타입(1) (0) | 2023.04.02 |
| [JS deep dive] this (0) | 2023.04.01 |
| [JS deep dive] 18장 함수와 일급 객체 (0) | 2023.03.31 |