자바스크립트는 기본적으로 Java와 C++과 같은 클래스 객체의 상속이란 개념을 사용하지 않고 prototype 모델의 상속을 지원한다. prototype은 객체를 생성하기 위해 사용되는 객체의 원형을 뜻하며 생성자 객체를 통해 객체가 생성 될 때, 생성자의 prototype 프로퍼티에 링크되어있는 프로토타입 객체들이 생성된 객체에 연결된다. prototype 프로퍼티는 함수 함수들이 가지고있는 속성으로서 생성자를 처음 정의할때엔 기본적으로 empty Object를 가르킨다.

(cf: prototype 프로퍼티는 1급 객체인 함수 객체만 가지고 있는 프로퍼티이므로 일반 객체에서 접근하려고하면 syntax error가 발생한다.)


prototype을 정리하기에 앞서 비교를 위해 생성자 함수를 이용한 객체 생성을 먼저 보면,

var Parent = function(){ this.f = function(){ console.log("HI"); } } Parent.f = function(){ console.log("HELLO"); } var A = new Parent; var B = new Parent; A.f(); B.f();

위 코드의 결과는 다음과 같다.
HI
HI

위에서 this.f는 프로토타입이 아닌 생성자 Parent의 프로퍼티이다. 즉 이 생성자를 통해 새로운 객체를 생성하면 this가 A,B객체에 바인딩이 되어 A,B객체의 프로퍼티가 되기 때문에, 아무리 생성자인 Parent의 f를 수정하더라도 생성된 객체인 A와 B의 f 프로퍼티는 변하지 않는다.(객체 생성 부분은 객체 생성 포스트를 참조하자)


다음은 프로토타입을 이용한 객체 생성이다.

var Parent = function(){ Parent.prototype.f = function(){ console.log("HELLO"); }; } var A = new Parent; var B = new Parent; A.f(); B.f(); Parent.prototype.f = function(){ console.log("HI"); }; A.f(); B.f();

위 코드의 결과값은 다음과 같다.

HELLO
HELLO

HI
HI

즉 생성자의 prototype 프로퍼티를 이용해 프로토타입 객체에 생성된 프로퍼티는, 그 생성자 객체에 의해 생성된 모든 객체가 그 프로퍼티를 공유한다. 생성자가 가지고 있는 프로토타입 객체의 프로퍼티가 변하면 그 생성자에 의해 생성된 모든 객체들의 함수도 같이 변한다. 이는 prototype의 값은 생성자가 가지고있는 f의 값을 객체 자신만의 프로퍼티로 가지고 있는 것이 아니라, 프로토타입 객체에 링크되어 직접 찾아가 사용하기 때문이다.

이를 이해하기 위해선, 생성자 함수 객체가 가지고있는 prototype 프로퍼티 말고도, 객체들이 가지고있는 [[Prototype]]프로퍼티의 존재를 알아야한다. 이는 개발자 도구에서 객체를 열어보면 __proto__프로퍼티로 표시되는 프로퍼티이다. 이 __proto__프로퍼티가 자신이 상속 받은 부모 객체의 prototype을 가르키는 프로퍼티이며, 객체는 자기 자신이 가지고있는 프로퍼티 말고도, __proto__의 링크의 연결되어있는 부모 함수객체의 prototype에 정의해놓은 객체나 변수들을 객체 자신의 것인 양 사용하는 것이다.

(객체들은 모두 가지고있는 [[Prototype]]프로퍼티(__proto__)와 함수(생성자) 객체만 가지고있는 prototype 프로퍼티 두개를 헷갈리지 않도록 조심하자, 가르키는 대상은 같지만 가지고 있는 객체는 다르다. ECMAScript에서 [[Prototype]]프로퍼티라고 명세 했지만 대부분의 브라우져에선 __proto__로 표시하고있다.)

여담으로 함수 객체는 상속을 위한 porototype 프로퍼티가 자신의 prototype 객체를 가리키는데, 반대로 prototype에서는 자기 자신을 포함하는 생성자 함수 객체를 가르키는 constructor를 가지고 있다. 밑에 사진에서 보면 그 관계를 자세히 알 수 있다.

크롬의 개발자 도구에서 확인한 객체의 프로퍼티이다.

__proto__프로퍼티(=[[Prototype]] 프로퍼티)가 자기를 생성한 Member 생성자 객체의 prototype을 가르키고 있다. 그리고 그 prototype이 가지고있는 프로퍼티중 constructor 프로퍼티는, 생성자 함수를 가르키는것을 확인할 수 있다.




위 사진처럼 prototype를 통해 상속되어 연속되어 이어진 형태를 프로토타입 체인(prototype chain)이라고 한다.
객체 에서 어떤 프로퍼티를 사용하려고 할때, __proto__를 따라 상속받은 프로토타입을 찾아가며 프로퍼티를 찾아나간다.
원하는 프로퍼티를 찾고자 하는 과정은 위 그림 순서대로 찾아간다.
객체 자신이 해당 프로퍼티를 가지고있지 않으면 __proto__를 따라 올라가 상위 프로토타입 객체에서 찾고, 없으면 또 __proto__를 따라 올라가 프로퍼티를 찾아나간다.
끝까지 탐색을 해나갔음에도 불구하고(Object.prototype까지 탐색) 원하는 프로퍼티를 찾지 못했을 경우에는 undefined를 반환한다.
이와 같이 찾아 체인을 따라 프로퍼티를 찾는 과정이 프로토타입 체이닝이다.


해당 프로퍼티가 객체 자신의 프로퍼티인지, prototype으로 상속받은 프로퍼티인지는 hasOwnProperty 메소드를 이용해 알 수 있다.
hasOwnProperty메소드는 모든 객체들의 조상인 Object.prototype에 정의 되어 있으며, 이 또한 프로토타입 체이닝을 통해 모든 객체가 사용 가능하다.


그외 Object.prototype(네이티브 프로토타입)도 프로퍼티를 추가해서 모든 객체들의 기능을 확장할 수 있는데(Object.prototype은 모든 객체의 조상이니 말이다) 이는 권장되는 방법은 아니다.



정리하자면 함수 객체는 자식에게 상속할 프로퍼티를 포함하는 prototype를 가지고 있다. prototype 프로퍼티는 객체의 원형을 담는 자신의 프로토타입 객체에 링크되고, 또한 객체를 생성할 때 객체의 [[Prototype]] 프로퍼티(__proto__)에 링크된다. (객체 생성 포스트를 참조하자, new 키워드를 이용해 생성될 때 연결된다.). 객체는 이 __proto__에 연결된 프로토타입 객체를 따라가 프로토타입에 정의된 프로퍼티를 객체 자신의 것처럼 사용할 수 있다. 부모 프로토타입에도 없을 경우, 부모가 상속받은 프로토타입을 찾아가며(__proto__프로퍼티를 통해 프로토타입 체인을 따라 올라가며) 해당 프로퍼티를 찾는다.

이렇게 따로 생성한 객체 말고도, Array나 Function 등의 객체들도 프로토타입의 상속을 기반으로 한다. Array나 Function이 내부적으로 가지고있는 메소드들은 바로 이 상속을 기반으로 가능한 것이다.

객체의 생성은 함수 객체(생성자 객체)에 의해 일어나지만, 상속에 관련된 모든 역할은 생성자 객체에 연결되어있는 프로토타입 객체에 의해 일어난다는 것을 기억하자. 

'Javascript' 카테고리의 다른 글

자바스크립트 - 실행 컨텍스트  (1) 2016.03.03
자바스크립트 - 객체 생성  (0) 2016.02.21
자바스크립트 - this 키워드  (0) 2016.02.04

+ Recent posts