본문 바로가기

개발/JavaScript

Javascript의 prototype 제대로 이해하기

출처:http://blog.naver.com/jjoommnn/130125787219
Javascript의 prototype 제대로 이해하기  Javascript 

2011/12/07 10:55

복사http://blog.naver.com/jjoommnn/130125787219

전용뷰어 보기

Ajax가 대중화 된 이후부터 javascript를 프로그래밍 언어로서 제대로 배우려는 노력들이 많아진것 같다.

그렇게 javascrpt를 공부하다가 보면 객체지향적으로 javascript를 사용하는 방법과 관련하여 prototype을 배우게 된다.

 

그럼 지금 알고 있는 javascript의 prototype에 관련된 지식으로 다음 코드를 보자.

var obj = {};

obj.prototype.foo = function() { alert('foo'); };

 

obj.foo(); //<-에러

위를 javascript 코드를 실행해 보면 에러가 발생한다. 즉 foo라는 함수를 찾을 수 없다는 에러다.

어, 이상하다. 객체의 속성을 엑세스 했을때, 그 객체의 자체 속성에서 못찾으면 그 객체의 prototype에서 찾는것 아니었나?

 

그래, 맞다. 그 객체의 속성을 찾을때 자신의 prototype이 아니라, new를 통해 생성한 base 객체의 prototype에서 찾기 때문에 그렇구나.

그런데 다음 코드를 또 보자.

function base() 

{

    ...

}

 

var obj = new base();

 

base.prototype = { foo : function() { alert('foo'); } };  //base.prototype.foo = function() { ... }  와 다름

 

obj.foo(); //<-에러

이번에도 obj에서 foo를 찾을 수 없다는 에러가 발생한다.

obj는 base를 기반으로 만들어 졌으니, base의 prototype을 나중에 바꾸어도 obj가 쓸 수 있어야 되는거 아닌가?

 

그러나 주석에도 썼지만 base.prototype.foo = function() { ... } 헝태로 코드가 작성되었다면 에러가 발생하지 않는다.

또한 base.prototype = { ... } 을 먼저 실행하고 var obj = new base()를 하게 되면, 역시 에러가 발생하지 않는다.

 

나참, 이거 나름대로 공부한다고 했는데 클래스를 가진 언어만 알고 있는 상태에서 이 prototype을 익히는 것이 만만치가 않다.

하지만 이렇게 prototype과 관련하여 아리송한 문제들이 생기는 이유는 뭔가 보이지 않은 것이 있었기 때문이었다.

 

결론부터 이야기 하면 다음과 같다.

javascript의 객체에는 프로토타입과 관련하여 위의 그림 처럼 2개의 연결고리를 가지고 있다.

 

prototype은 익히 알고 있던 그 속성이다. 프로그램적으로 세팅하고 바꾸고 할 수 있는 바로 그 prototype 이다.

반면 __proto__는 내부에 있기는 하지만 내부적만 사용되며 프로그램적으로 접근할 수 없는 속성이다.

이 두 속성의 차이는, __proto__는 물려받은 상위의 프로토타입 객체와 연결되는 고리이며, prototype은 하위에 물려줄 프로토타입 객체에 대한 연결고리라는 것이다.

이 두 속성은 밀접하게 연관되어 있는데, 물려받으려면 물려주는 누군가가 있어야 하기 때문이다.

 

prototype 속성에 관해서는 여느 책이나 자료에서 많이 보아왔던 것이며, 앞서도 이야기 했지만 프로그램적으로 자유롭게 세팅하거나 교체할 수 있다.

그러나 __proto__는 프로그램적으로 접근할 수 없으며, 단지 new 연산자에 의한 객체 생성과정 중에만 세팅된다.

 

다음과 같은 코드는

 

var obj = new base();

새로운 객체를 생성하고 그 새로 생성된 객체의 __proto__속성에다가, base의 prototype 속성에 연결되어 있던 객체를 연결시켜주는 것이다.

 

여기서 'base의 prototype 객체'라고 이름을 붙였지만, 이것 역시 그냥 object인 객체다. 특별한 타입의 뭔가가 아니다.

base의 prototype에 연결되어 있던 객체가 new 연산자를 통해서 새롭게 생성된 객체의 __proto__에 연결되는 것이다.

 

이 __proto__에 의한 연결고리는 특히나 중요한데, 바로 객체의 속성 검색시에 검색 체인을 형성하기 때문이다.

즉 어떤 객체의 속성을 엑세스 할 때, 그 객체의 내부에서 찾아보고 없으면 __proto__를 따라 상위 객체로 가면서 검색을 하게 된다.

 

그러나 이 검색 과정중에 prototype 속성에 의한 연결고리로는 검색이 이루어지지 않는다는 것을 알아야 한다.

위의 첫번째 예제에서 에러가 발생하는 이유가 바로 이 때문이었다.

 

이 __proto__에 의해 연결된 개체를 따라가면서 이루어 지는 검색은 최상의 Object까지  이루어진다.

최상위 Object까지 해서 전체적인 그림을 살펴보면 다음과 같이 된다.

        [위 그림은 잘못된 그림이므로 수정된 글을 참조하기 바람]

 

그러면 위의 2번째 예제에서 에러가 발생한 이유는 무엇인지도 대충 감이 올것 같은데, 그림으로 그려보면 다음과 같은 상황이 되었기 때문이다.

 

즉 new base()를 할 당시에는 obj의 __proto__와 base의 prototype이 같은 객체를 가리키고 있었지만, base의 prototype을 완전히 교체해 버린 상태가 된 것이다. 그러니 base.prototype에 추가된 속성이 obj를 통해 엑세스 될 수 없는 것이다.

base.prototype을 완전히 다른 객체로 바꾸는 것이 아니라, 주석처리 했던 것 처럼 추가되는 형태였다면 에러는 발생하지 않는 것이다.

 

이렇게 놓고 보니 obj객체와 base객체간의 관계는 전혀 무관한 관계가 되었다.

따라서 다음과 같은 결과가 나온다.

function base() 

{

    ...

}

 

var obj = new base();

 

base.prototype = { ... };

 

alert( obj instanceof base );  //<-- false를 출력 

 

즉 javascript에서 객체들간의 관계는 prototype에 의해서 묶이는 것이다.

 

그럼 javascript에서 상속을 구현하기 위한 코드가 어떻게 연결되는지 따져보자.

javascript에서 상속은 다음과 같은 형태로 구현된다.

function base()

{

    //base 생성자에서 속성 추가

    ...

}

 

//base의 prototype에 속성추가

base.prototype.foo = ...

...

 

function sub()

{

    //sub 생성자에서 속성 추가

    ...

}

 

//sub의 prototype에 new base() 로 생성한 객체대입

sub.prototype = new base();

 

//생성자 함수를 sub로 바꿈

sub.prototype.constructor = sub;

 

//sub 객체 생성

var obj = new sub();

위의 코드에 따른 객체의 상태를 그림으로 살펴보면 다음과 같이 된다.

화살표가 조금 어지럽게 연결되긴 했지만, 크게 어렵지 않게 그림을 이해 할 수 있을 것이다.

 

위의 그림에 따라 obj의 속성을 엑세스 할때 검색 경로를 따져보자. 상속의 개념이 적용되는지 말이다. 검색은 __proto__를 따라서 올라가기만 하면된다.

 

(1)일단 obj 내부의 속성에서 찾을 텐데, 이 obj 내부 속성은 sub의 생성자에서 추가한 속성들일 것이다.

(2)sub 생성자에서 추가한 속성이 없으면 base의 생성자에서 추가한 속성에서 찾게 될 것이다.

(3)그래도 없으면 base의 prototype 객체에서 찾게 될 것이다.

(4)그래도 없으면 Object의 prototype에서 찾게 될 것이다.

 

자연스럽게 자식에게서 상위 부모로 검색경로가 진행되는 것을 알 수 있다.

 

[참고문헌]

자바스크립트 성능 최적화, 니콜라스 자카스 저/한선용 역 | 한빛미디어, 2011년 9월