Prototype in Javascript
prototype in Javascript
![What is difference between .__proto__, .prototype, [[Prototype]] /images/prototype_in_js.png](/images/prototype_in_js.png)
What is difference between __proto__
, prototype
, [[Prototype]]
TL;DR
.__proto__
: getter of [[Prototype]][[Prototype]]
: Internal Property.prototype
- function 정의: js는 자동으로 함수 정의할때,
함수.prototype
에{ constructor: 함수 }
인 object를 넣어준다. - Constructor: js는 자동으로 constructor로 instance를 생성(new operator)할 때,
const f = new F()
라고 한다면,f.__proto__ = F.prototype
를 시행한다.
- function 정의: js는 자동으로 함수 정의할때,
FYI,
__proto__
를 직접적으로 활용하는 방식은 (deprecated)되었습니다.
|
|
__proto__
vs prototype vs [[Prototype]]
Function
Javscript에서 Function은 사실 Object입니다. 더 정확히 말하자면 Function이란 키워드는 Object에 [[Call]]
(ECMA-262) internal property를 추가한 object입니다.
여기서 말하는 Function은 function
이라는 함수를 생성할 때, 사용하는 예약어가 아닌 위 콘솔에서 확인할 수 있는 미리 생성되어있는(Built-in) object를 뜻합니다.
즉, 모든 function들은 Function(Built-in Function Object)의 instance입니다. 쉽게 설명해서 function = new Function()
라고 생각할 수 있습니다.
또한 JS에서 function F() {}
처럼 function을 정의할 때, 내부적으로 prototype
이라는 property를 추가하고, 이 값으로 { constructor: self }
인 object를 추가합니다.
JS의 Built-in들은 native source code로 정의되어있어, 매우 빠릅니다. (c++)
|
|
3 Functions compare
function declaration
(함수 선언식)외에도 추가로 2가지의 함수에 대해서도 동일하게 동작하는지 확인해보겠습니다.
함수를 생성하는 방법은 3가지입니다.
- Function declaration (function a() {})
- Function expression (const f = function() {})
- Arrow function (const f = () => {})
먼저 Function expression입니다. 결론부터 말씀드리면, function declaration과 동일하게 동작합니다.
|
|
다음으로는 Arrow Function입니다. 결론부터 말씀드리면, .prototype을 받지 못해 constructor필드가 존재하지 않게되어 생성자(Constructor)로서의 기능을 하지 못합니다. 그렇기 때문에 new
연산을 통해서 instance를 생성할 수 없습니다.
|
|
다만 __proto__
, 즉 [[Prototype]]
property는 기존의 function declaration의 [[Prototype]]
과 동일한 property를 가지고 있습니다.
정리하면, function declaration방식과 function expression 방식은 차이가 없이 동작하며, arrow function는 prototype을 받지 못해, constructor가 존재하지 않습니다.
Constructor
모든 function들은 new
라는 operator를 통해서 constructor
의 기능을 할 수 있습니다.
new키워드를 통해서 instance가 생성될 때, js에서는 추가적으로 constructor.property를 instance.__proto__
안에 넣어줍니다.
|
|
즉 아래 2가지로 해석가능합니다. 이때 2번째 경우는 F === F.prototype.constructor
이기 때문에 가능합니다.
instance.__proto__
===F.prototype
instance.__proto__
===F.prototype.constructor.prototype
instance의 __proto__는 F.prototype을 가리키고, F.prototype.constructor는 F와 같기 때문에, instance.__proto__는 F.prototype과 F.prototype.constructor.prototype과 같습니다.
instance.constructor === instance.__proto__.constructor
js에서는 instance가 생성될 때, constructor라는 property를 추가시켜, F.prototype.constructor를 가리키고 있습니다.
|
|
즉 instance의 constructor property는 reference타입이라는 것을 알 수 있습니다.
The prototype chain
js는 Built-in Object가 존재하며, User Defined Object들은 결국 빌트인 Object까지 chain을 타고 올라가며, 최종적으로 Object.[[Prototype]] === null에서 chainning이 마무리됩니다.
The chain of objects connected by the
__proto__
property is called the prototype chain.
즉 js는 상속을 __proto__
필드를 사용해서 구현했으며, 실제로는 reference로 저장되기 때문에, singleton object들을 공유해서 상속하는 방식으로 되어있습니다.
아래는 위의 다이어그램을 js코드로 간단하게 표현해봤습니다.
|
|
__proto__
vs [[Prototype]]
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
주의: 객체의 [[Prototype]]을 변경하는 것은 최신 JavaScript 엔진이 속성 접근을 최적화하는 방식의 특성상 모든 브라우저 및 JavaScript 엔진에서 매우 느린 작업입니다. 상속 구조를 변경하는 것이 성능에 미치는 영향은 미묘하고 광범위하며,
obj.__proto__ = ...
문에 소요되는 시간 뿐만 아니라 [[Prototype]]이 변경된 객체에 접근할 수 있는 모든 코드들에 대해서도 영향을 줄 수 있습니다. 성능에 관심이 있다면 객체의 [[Prototype]] 설정을 피해야 합니다. 대신 Object.create()를 사용하여 원하는 [[Prototype]]으로 새 객체를 만드세요.
주의:
Object.prototype.__proto__
는 오늘날 대부분의 브라우저에서 지원되지만, 그 존재와 정확한 동작은 오직 웹 브라우저와의 호환성을 보장하기 위한 레거시 기능으로서 ECMAScript 2015 사양에서 비로소 표준화되었습니다. 더 나은 지원을 위해 대신 Object.getPrototypeOf()를 사용하세요.
그러니, 직접적인 __proto__
보다는 Object.getPrototypeOf()를 사용하는 것이 권장된다.
Conclusion
__proto__
: getter of [[Prototype]].- prototype: 함수에서는 constructor기능을 위해, 사용되며, object는 inheritance를 위해 사용되는 필드.
- [[Prototype]]: proto chain을 사용한 상속(코드 공유)를 위해 사용되는 internal property.