프로토타입 객체

Park, Jinyong·2020년 5월 1일
1

Small, but Valuable.

목록 보기
16/19

자바스크립트는 프로토타입 기반 언어라고 한다. 객체를 다룰 때 프로토타입을 사용하기 때문이다. 최근 추가된 class 문법 또한 다른 언어와 달리 프로토타입 기반이다. 그러므로, 자바스크립트를 다룰려면 프로토타입을 이해하는 것이 중요하다.

자바스크립트에서 생성된 모든 객체는 [[Prototype]]이라는 숨겨진 내부 속성을 갖고 있다. 이 속성은 자신의 프로토타입 객체를 가리킨다. 프로토타입 객체에 접근하는 데 [[Prototype]] 속성은 사용할 수 없으므로, 그 존재를 알아두기만 하고 이 이후로 더 이상 언급하지 않겠다.

__proto__ 개념을 언급할 것인데, 이는 표준이 아니라서(참고1, 참고2) Object.getPrototypeOf()Object.setPrototypeOf()로 대체된다. 하지만, __proto__는 많은 브라우저에서 구현되어 사실상 표준이 되었기 때문에 이하 내용에선 __proto__를 사용한다.


프로토타입 객체

자바스크립트에서 모든 객체는 Object라는 생성자 함수로부터 시작된다.

console.log(typeof Object); // function

모든 함수는 prototype이라는 속성을 가지고 있는데, 이 속성은 함수가 정의될 때 같이 생성되는 프로토타입 객체를 참조한다. 프로토타입 객체는 constructor라는 속성을 가지고 있고, 이 속성은 생성자 함수를 참조하고 있다.
다시 말해, prototype은 프로토타입(prototype) 객체를 가리키고, constructor는 생성자(constructor) 함수를 가리킨다. 즉, 서로를 상호 참조하고 있다.

console.log(Object.prototype); // {constructor: f}
console.log(Object.prototype.constructor === Object); // true

모든 함수라고 언급했지만, 프로토타입 객체는 생성자 함수일 때 의미가 있는 객체이다. 즉, new 연산자로 새로운 객체가 생성될 때, 그 생성된 객체도 프로토타입 객체를 가리킨다. 우리가 흔히 사용하는 객체도 Object의 프로토타입 객체를 참조하고 있다. 우리는 객체를 생성할 때 객체 리터럴을 사용한다. 이는, 주석의 방법과 동일하다.

let obj = {}; // let obj == new Object();

생성자 함수는 prototype 속성이 프로토타입 객체를 참조하고 있다고 했다. 그와 달리,new 연산자로 생성된 객체는 __proto__ 속성이 프로토타입 객체를 참조하고 있다.

let obj = {}; // let obj == new Object();
console.log(obj.__proto__); // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
console.log(Object.prototype === obj.__proto__); // true
console.log(Object === obj.__proto__.constructor); // true

생성자 함수의 프로토타입 객체와 생성된 객체의 __proto__ 속성이 참조하는 프로토타입 객체가 동일한 것을 확인할 수 있다.

같은 생성자 함수로부터 생성된 객체는 모두 같은 프로토타입 객체를 참조하고 있다.

let obj1 = {}; // let obj = new Object();
let obj2 = {}; // let obj = new Object();
console.log(obj1.__proto__ === obj2.__proto__); // true

모든 객체가 공유해야할 정보(e.g. 메서드)는 프로토타입 객체에서 관리하는 것이 유리하다고 볼 수 있다.

프로토타입 체인

우리가 생성자 함수에 메서드를 추가할 때 아래와 같은 방법으로 한다.

function Person(name) {
  this.name = name;
}
Person.prototype.sayHi = function() { console.log(`Hi! I'm ${this.name}.`); };
let park = new Person('Park');
park.sayHi(); // Hi! I'm Park.

console.log(park);
// Person {name: "Park"}
//  - name: "Park"
//  - __proto__: Object

park의 내부를 살펴보면 name 속성과 __proto__ 속성만 확인할 수 있다. sayHi()는 어떻게 사용할 수 있을까? 우리가 객체의 속성에 접근할 때, 자바스크립트 내부에선 보이지 않는 동작이 실행된다. 일단, 해당 객체에서 속성을 찾는다. 만약 속성이 없다면, 해당 객체의 프로토타입 객체를 확인한다. 또 속성이 없다면 해당 프로토타입 객체의 프로토타입 객체에서 확인한다. 이를 찾을 때까지 반복한다. 이렇게 연결되어 있는 모습을 프로토타입 체인이라고 하고, 프로토타입 체인을 타고 올라가며 속성을 찾는 동작 방식을 프로토타입 상속이라고 한다.

위에서 new로 생성된 객체는 생성자 함수의 프로토타입 객체를 참조하고 있다고 했다. 프로토타입 상속을 적용하면 굳이 우리가 직접 접근하지 않더라도 알아서 찾아낸다.

let obj = {}; // let obj = new Object();
console.log(Object === obj.__proto__.constructor); // true
console.log(Object === obj.constructor); // true

[참고]
모던 JavaScript 튜토리얼
생활코딩 - prototype vs proto
생활코딩 - __proto__
생활코딩 - 상속
ZeroCho Blog - 생성자와 프로토타입
ZeroCho Blog - 객체 상속
stack overflow - __proto__, when will it be gone? Alternatives?
stack overflow - What is the end of prototype chain in javascript — null or Object.prototype?
상속과 프로토타입 - MDN Document

0개의 댓글