Prototype 이제는 이해하자
prototype은 왜 어려울까?
C++, Java와 같은 클래스
기반 객체지향 언어와 달리 자바스크립트는 프로토타입
기반 객체지향 언어입니다. 프로토타입을 사용하여 객체지향을 추구하기 때문에 자바스크립트를 사용함에 있어 프로토타입을 이해하는 것은 중요합니다. 최근 ECMA6 표준에서 Class
문법이 추가되었지만 C++, Java에서 말하는 클래스가 아닌 프로토타입
을 기반으로 하여 만들어진 문법입니다.
자바스크립트의 프로토타입을 처음 공부하면서 prototype, [[prototype]], _proto_, 객체, 함수, prototype chain 과 같은 용어들을 접하게 되는데 공부할수록 서로 뒤엉켜지고, 모르는 것도 아닌 그렇다고 제대로 알고 있는것도 아닌 어중간한 상태가 됩니다.
자바스크립트를 사용한 경험이 있으시다면 아래의 코드와 같은 형태를 경험한적이 있으실겁니다. 지금부터 아래의 코드가 어떤 원리로 동작하게 되는지 알아보겠습니다.
먼저 프로토타입에 대해 이해하기 위해서는 객체(object)는 함수(function)로부터 시작된다
라는 것을 알아야 합니다. 이는 prototype을 이해하는데 많은 도움을 줍니다.
객체(object)는 함수(function)로부터 시작된다
자바스크립트에서 primitive를 제외하고는 모두 객체(object)입니다.
앞으로 등장하는 Object와 Function은 function(즉, 생성자)입니다. object는 객체를 의미합니다.
다음의 코드를 분석하기 전 객체(object)는 함수(function)로부터 시작된다
라는걸 다시한번 기억하겠습니다.
1 | function Book() { } // 함수 |
위의 코드에서 Book이라는 함수를 통해서 jsBook이라는 객체를 생성했습니다. 이때 Book 함수를 생성자
라고 합니다. 생성자는 새로 생성된 객체를 초기화하는 역할을 합니다. 코어 자바스크립트는 기본 타입에 대한 생성자를 내장하고 있는데 이는 다음 코드를 통해 확인이 가능합니다.
1 | var cssBook = {}; // 생성자 선언 없이 객체 생성 |
위에서는 리터럴 방식을 사용하여 객체를 생성하였습니다. 리터럴 방식 또한 결과적으로는 함수를 통하여 객체를 생성하게 됩니다. 자바스크립트 엔진이 해당 리터럴을 다음과 같이 해석합니다.
1 | var cssBook = new Object(); // 객체 생성 |
따라서 결과적으로는 리터럴 방식으로 객체를 생성할때도 Object라는 함수(생성자)를 통해서 객체를 생성하게 됩니다. Object 뿐만 아니라 Array, Function, Date, RegExp 모두 함수입니다.
배열도 객체이기 때문에(자바스크립트 배열은 객체의 특별한 형태입니다. 프로퍼티 이름이 정수로 사용되며, length 프로퍼티를 가집니다.) 객체를 생성할때와 마찬가지로 배열(객체)의 생성에도 함수가 관여하게 됩니다. 따라서 무심코 사용했던 배열의 리터럴 표현도 결국에는 자바스크립트 엔진이 다음과 같이 해석합니다.
1 | var books = ['html', 'css', 'js']; // 배열(객체) 생성 |
이제 객체(object)는 함수(function)로부터 시작된다
라는 것을 알 수 있습니다.
함수(function) 생성시 발생하는 일
객체(object)는 함수(function)로부터 시작되기 때문에 사용자가 객체를 생성하기 위해 먼저 함수를 정의하게 됩니다. 이때 발생하는 일에 대해 알아보겠습니다. 여기서는 2가지를 기억해야 합니다.
1.함수를 정의하면 함수가 생성되며 Prototype object
가 같이 생성 됩니다. 생성된 Prototype object는 함수의 prototype
속성을 통해 접근할 수 있습니다. (Prototype object같은 경우 함수 생성시에만 됩니다. 일반 객체 생성시에는 생성되지 않습니다.)
2.함수의 생성과 함께 생성된 Prototype object
는 constructor
와 __proto__
를 갖고 있습니다. (cover property를 추가한것 처럼 사용자 임의로 추가 가능합니다.) constructor는 생성된 함수를 가리키며(여기서는 function Book을 가리킵니다.) **_proto_**는 Prototype Link
로서 객체가 생성될 때 사용된 생성자(함수)의 Prototype object를 가리킵니다. Prototype Link는 뒤에서 자세하게 알아보겠습니다.
다이어그램을 통해 확인하면 다음과 같습니다.
객체(object) 생성시 발생하는 일
이번에는 객체 생성시 발생하는 일에 대해 알아보겠습니다. 조금 전에 정의한 Book 함수(생성자)를 사용하여 jsBook이라는 객체를 생성해 보겠습니다.
생성자(함수)의 몸체 부분에 어떠한 코드도 작성하지 않았는데 이를 통해 생성한 jsBook 객체가 __proto__
라는 프로퍼티를 갖고있습니다.
여기서 _proto_ 는 Prototype Link
로서 **객체의 생성에 쓰인 생성자 함수의 Prototype object
**를 가리키고 있습니다. 그렇기 때문에 Book 생성자 함수와 함께 생성된 Prototype object에 추가한 cover라는 프로퍼티가 보이는것을 확인할 수 있습니다.
조금 더 이해하기 쉽게 다이어그램으로 확인하면 다음과 같습니다.
다이어그램에서도 확인할 수 있다시피 prototype
property(함수 생성시 함께 생성된 Prototype object를 가리킴)는 함수객체만 가지며 __proto__
는 객체라면 모두 갖고 있습니다.
이제 프로토타입 체인(Prototype Chain)에 대해 이해할 수 있는 준비가 되었습니다.
프로토타입 체인(Prototype Chain)
결론부터 말씀드리면 프로토타입 체인
은 객체의 property를 사용할때 해당 property가 없다면, __proto__
property를 이용해 자신의 생성에 관여한 함수(생성자 함수)의 Prototype object
에서 property를 찾습니다. 만약 Prototype object에도 해당 property가 없다면 다시 Prototype object의 _proto_ property를 이용해 Prototype object에서 property를 찾습니다. 이렇게 계속 반복이 이루어지며 해당 property를 찾게 된다면 값을 반환하고 찾지 못한다면 undefined를 반환합니다. 이렇게 __proto__
property를 통해 상위 프로토타입과 연결되어 있는 형태를 프로토타입 체인(Chain)이라고 합니다.
프로토타입 체인에 대해 알게되었으니 다시한번 처음 코드를 살펴보겠습니다.
이제 어떻게 jsBook에 cover라는 property를 추가하지 않았는데도 결과가 출력되는지 이해할 수 있습니다. 다음과 같이 동작할 것입니다.
또한 다음과 같이 프로토타입 체인의 최상위는 Object이기 때문에 Object.prototype의 property들을 모두 사용할 수 있습니다. 자주 사용하는 toString()과 valueOf() 모두 Object.prototype에 선언되어 있습니다.
(Book Prototype object는 객체이기 때문에 Object 생성자가 사용될 것입니다. 따라서 Book Prototype object의 **_proto_**는 Object Prototype object를 가리키게 됩니다.)
Prototype object
와 __proto__
그리고 프로토타입 체인
에 대해 이해하였으니 다음과 같은 코드도 이해할 수 있습니다. 잘 이해가 되지 않는다면 위의 다이어그램을 참고해보시기 바랍니다.
번외
혹시 다이어그램을 보면서 function Book의 _proto_ 는 무엇을 가리키고 있는지 궁금해 하셨을 분들을 위해 추적해보았습니다.
다음 코드를 도식화 하면 다음과 같은 다이어그램이 나오게 됩니다.
간단하게 포스팅을 하려했는데 주제가 주제인지라 길어졌습니다. 저도 프로토타입을 처음 공부하면서 어려움을 많이 겪었는데 조금이나마 도움이 되었으면 좋겠습니다.