본문 바로가기
Javascript

[Javascript] Javascript 프로토타입 (Prototype)

by parkjp 2017. 8. 31.

 

1. Javascript 프로토타입 (prototype)?

 

 명세에 따르면 자바스크립트 객체는 [[prototype]]이라는 내부 프로퍼티가 있고 다른 객체를 참조하는 단순 레퍼런스로 사용한다.

 

2017/08/31 - [Javascript] - [Javascript] Javascript 프로퍼티 서술자 (Getter/Setter)

 

위 링크 글 exObject.a처럼 객체 프로퍼티 참조 시 [[Get]]이 호출된다고 했었다. 또한 exObject에 a라는 프로퍼티가 없으면 [[prototype]] 링크를 따라가면서 찾는다고 했었다.

 

아래의 예제에서 Object.create(..) 함수는 특정 객체의 [[Prototype]]링크를 가진 객체를 생성한다.

 

var anotherObject = {
    a: 2
};

// anotherObject에 연결된 객체를 생성한다.
var exObject = Object.create(anotherObject);
exObject.a; // 2

 

exObject는 anotherObject와 [[Prototype]]이 링크되었다. 분명히 exObject.a란 프로퍼티란 없지만 anotherObject에서 2라는 값을 대신 찾아 결과값으로 반환한다.

 

- Object.prototype 

 

 [[Prototype]] 체인이 끝나는 지점은 정확히 어디일까?

일반 [[Prototype]] 체인은 결국 내장 프로토타입 Object.prototype에서 끝난다. 모든 자바스크립트 객체는 Object.prototype 객체(최상위 [[Prototype]] 체인은 Object.prototype 이다.)의 '자손'이므로 Object.prototype에는 자바스크립트에서 두루 쓰이는 .toString()이나 .valueOf() 같은 다수의 공용 유틸리티가 포함되어 있다.

 

- 프로퍼티 가려짐 (shadowing)

 

exObject.foo = 'bar';

 foo라는 평범한 데이터 접근 프로퍼티가 exObject에 직속된 경우 이 할당문은 기존 프로퍼티 값을 고치는 단순한 기능을 할 뿐이다. foo가 exObject에 직속된 프로퍼티가 아니면 [[Get]]처럼 [[Prototype]] 체인을 순회하기 시작하고, 그렇게 해도 foo가 발견되지 않으면 그제서야 foo라는 프로퍼티를 exObject 객체에 추가한 후 주어진 값을 할당한다.

 

여기서 foo라는 프로퍼티명이 exObject 객체와 이 객체를 기점으로 한 [[Prototype]] 체인의 상위 수준 두 곳에서 동시에 발견될 때 이를 가려짐( shadowing )이라 한다. exObject에 직속한 foo 때문에 상위 체인의 foo가 가려지는 것이다.

 

다음 아래의 경우를 보자.

exObject에 직속한 foo는 없으나 exObject [[Prototype]] 체인의 상위 수준에 foo가 있을 때 exObject.foo = 'bar' 할당문의 실행 결과 다음 세 가지 경우의 수가 따른다.

 

1) [[Prototype]] 체인의 상위 수준에서 foo라는 이름의 일반 데이터 접근 프로퍼티가 존재하는데, 읽기 전용이 아닐 경우(writable : true), exObject의 직속 프로퍼티 foo가 새로 추가되어 결국 '가려짐 프로퍼티'가 된다.

 

2) [[Prototype]] 체인의 상위 수준에서 발견한 foo가 읽기 전용(writable : false)이면 이 프로퍼티를 세팅하거나 exObject 객체에 가려짐 프로퍼티를 생성하는 일은 일어나지 않는다. 엄격 모드에서는 에러가 나며, 비엄격 모드에서는 조용히 무시된다.

 

3) [[Prototype]] 체인의 상위 단계에서 발견된 foo가 세터일 경우 항상 이 세터가 호출된다. exObject에 가려짐 프로퍼티 foo를 추가하지 않으며 foo 세터를 재정의하는 일 또한 없다.

 

2, 3번에서 foo를 가리려면 '=' 할당 연산자를 쓰면 안되고 Object.defineProperty(..) 함수를 사용해서 foo를 추가해야 한다.

 

2. Javascript 생성자

 

function Foo() {
    // ...
}

Foo.prototype.constructor === Foo; // true

var a = new Foo();
a.constructor === Foo; // true

 

 

Foo.prototype 객체에는 기본적으로 열거 불가한 공용 프로퍼티 .constructor가 세팅되는데, 이는 객체 생성과 관련된 함수(Foo)를 다시 참조하기 위한 레퍼런스다.

 

위에서 Foo함수 앞에 new를 붙여 호출하였다. 그렇담 Foo는 생성자일까?

사실 Foo는 '생성자'가 아닌 그냥 함수일 뿐이다. 함수는 결코 생성자가 아니지만 그 앞에 new를 붙여 호출하는 순간 이 함수는 '생성자 호출'을 한다. new 키워드는 일반 함수 호출을 도중에 가로채어 원래 수행할 작업 외에 객체 생성이라는 잔업을 더 부과하는 지시자다.

 

 

 

 

 

 

참조 저서 : 카일 심슨, You Don't Know JS this & Object Prototypes, 한빛미디어, 117쪽

반응형