프로토타입과 포로토타입 상속
by JerryChu프로토타입에 대한 개념을 다시 학습하며 작성하였습니다. 이 글은 자바스크립트 프로토타입 링크 를 참고하여 작성되었습니다.
자바스크립트 프로토타입 상속
- 자바스크립트 객체는 숨긴 속성인 프로토타입을 갖음
- 이 값은 null 또는 다른 객체에 대한 참조가 됨
- 다른 객체를 참조하는 경우 참조 대상을 포로토타입이라고 부름
자바스크립트 프로토타입의 동작 방식
- 객체에서 속성을 읽을 때 해당 속성이 없다면 프로토타입의 속성을 찾음
- 프로그래밍에서는 이런 동작 방식을 프로토타입 상속이라고 부름
- [[Prototype]] 속성은 내부 속성이고, 숨김 속성이지만 다양한 방법을 통해 개발자가 설정 가능
let popupState = {
isOpen: false,
open: function () {
console.log('popup opening');
this.isOpen = true;
},
close: function () {
console.log('popup closing');
this.isOpen = true;
},
};
let somethingPopup = {
name: 'Something Popup',
__proto__: popupState,
};
위 코드와 같이 proto 를 사용하면 값을 설정 가능
- proto는 Prototype과 다름
- proto는 Prototype의 getter이자 setter
- 최근 proto를 통한 프로토타입을 설정하지 않고 Object.getPrototypeOf, Object.setPrototypeOf을 사용해서 get, set 설정을 함
아래 코드에서 somethingPopup에서 속성을 얻고 싶은데 없다면 popupState의 속성을 얻게 됨
let popupState = {
isOpen: false,
open: function () {
console.log('popup opening');
this.isOpen = true;
},
close: function () {
console.log('popup closing');
this.isOpen = true;
},
};
let somethingPopup = {
name: 'Something Popup',
__proto__: popupState,
};
console.log(somethingPopup.isOpen); // false
somethingPopup.open();
console.log(somethingPopup.isOpen); // true
- somethingPopup의 프로토타입은 popupState
- somethingPopup는 popupState의 상속을 받는다고 할 수 있음
- 여기서 somethingPopup 객체의 isOpen, open, close는 상속 속성이라고 함
그렇다면 자바스크립트의 프로토타입 체인은 무엇인가?
let popupAction = {
open: function () {
console.log('popup opening');
this.isOpen = true;
},
close: function () {
console.log('popup closing');
this.isOpen = false;
},
};
let popupState = {
isOpen: false,
__proto__: popupAction,
};
let somethingPopup = {
name: 'Something Popup',
__proto__: popupState,
};
console.log(somethingPopup.isOpen); // false
somethingPopup.open(); // popup opening
console.log(somethingPopup.isOpen); // true
somethingPopup.close(); // popup closing
console.log(somethingPopup.isOpen); // false
위코드를 보며 중요한 포인트는 프로토타입 체인 탐색에 있음. 어떤 프로퍼티나 메서드를 찾을 때, JS는 자신 → 프로토타입 → 상위 프로토타입 순서로 탐색함.
메서드 안의 this: 프로토타입에서 정의된 메서드라도, 호출한 객체(this) 에 프로퍼티를 직접 생성/변경함. 즉, open/close는 popupAction에 정의돼 있지만, 실제 isOpen은 somethingPopup 객체에 생김. 따라서 각 객체는 같은 "동작(open/close)"을 공유하지만, 상태(isOpen)는 독립적으로 가질 수 있음.
프로토타입 체이닝에는 세 가지 제약사항
- 순환참조가 허용되지 않음.
- __proto의 값은 null 또는 객체만 허용함, 여기서 객체는 오직 하나만의 [[Prototype]]만 있을 수 있음
- 직접적으로 메서드 할당했을 경우 프로토타입의 메서드가 동작하지 않음
let popupAction = {
open: function () {
console.log('popup opening');
this.isOpen = true;
},
close: function () {
console.log('popup closing');
this.isOpen = false;
},
};
let popupState = {
isOpen: false,
__proto__: popupAction,
};
let somethingPopup = {
name: 'Something Popup',
__proto__: popupState,
};
somethingPopup.open = function () {
console.log('somethingPopup custom open');
};
console.log(somethingPopup);
somethingPopup.open();
console.log(somethingPopup.isOpen);
getter와 setter를 통한 호출
let userBase = {
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
set fullName(value) {
[this.firstName, this.lastName] = value.split(' ');
},
};
let user = {
firstName: 'Chu',
lastName: 'hoon',
__proto__: userBase,
};
console.log(user.fullName); // Chu hoon
user.fullName = 'Park Ji-sung';
console.log(user.fullName); // Park Ji-sung
this는 프로토타입의 영향을 받지 않는다
this는 자신의 객체를 가르킴
let modalAction = {
open: function () {
console.log('modal opening');
this.isOpen = true;
},
close: function () {
console.log('modal closing');
},
};
let specialModal = {
name: 'Special Modal',
__proto__: modalAction,
};
console.log(specialModal.isOpen); // undefined
specialModal.open(); // modal opening
console.log(specialModal.isOpen); // true
반복문 차이
console.log('------------------------');
console.log('for in');
for (let key in specialModal) {
console.log(key);
}
// --------------------------------
// for in
// name
// isOpen
// open
// close
console.log('------------------------');
console.log('Object.keys');
console.log(Object.keys(specialModal));
// --------------------------------
// Object.keys
// [ 'name', 'isOpen' ]
사용하는 객체는 Prototype을 가지고 있다
let obj = {};
alert(obj.__proto__ === Object.prototype); // true
alert(obj.toString === obj.__proto__.toString); //true
alert(obj.toString === Object.prototype.toString); //true
// Object.prototype.__proto__ // null
다양한 내장 객체의 프로토 타입
- Array, Date, Function 내장 객체들 또는 프로토타입에 메서드를 저장함
- 함수, 배열의 프로토타입 체인은 어떻게 되어 있나요?
원시 값
-
원시 타입의 값의 속성에 접근하려고하면 내장 생성자 String, Number, Boolean을 사용하는 임시 래퍼 객체가 생성됨. 임시 래퍼 객체는 메서드를 제공 후 사라짐
-
null, undefined는 래퍼 객체가 없음
-
내장 프로토타입 수정은 가능하지만 되도록 변경하지 않아야함
-
원하지 않는 동작을 실행시킬 수 있고, 자바스크립트 엔진이 수정 전에 최적화 되어 있기 때문에 속도 차이가 나게 됨