關於 __proto__
屬性,MDN 上的解釋是這樣的[1]:javascript
The __proto__ property of Object.prototype is an accessor property (a getter function and a setter function) that exposes the internal [[Prototype]] (either an object or null) of the object through which it is accessed.java
便是說,__proto__
屬性指向了實例對象的原型 Constructor.prototype
。那麼,這個屬性裏隱藏着怎樣的黑魔法呢?es6
最近看 ECMAScript 6 的 spec,發現了一些有意思的東西,好比 class
章節:express
14.5.14 Runtime Semantics: ClassDefinitionEvaluation[2.1]
With parameter className.
ClassTail : ClassHeritageopt { ClassBodyopt }
...babel6.g (for class heritage)
If superclass has a [[FunctionKind]] internal slot whose value is "generator", throw a TypeError exception.函數
Let protoParent be Get(superclass, "prototype").ui
ReturnIfAbrupt(protoParent).this
If Type(protoParent) is neither Object nor Null, throw a TypeError exception.lua
Let constructorParent be superclass.spa
7. Let proto be ObjectCreate(protoParent).
...
12. Let constructorInfo be the result of performing DefineMethod for constructor with arguments proto and constructorParent as the optional functionPrototype argument.
...
16. Perform MakeConstructor(F, false, proto).
...
18. Perform CreateMethodProperty(proto, "constructor", F).
...
這幾行規定了類繼承(class SubClass extends SuperClass {}
)的行爲,除了衆所周知的 SubClass.prototype = Object.create(SuperClass.prototype)
之外,還作了一件有趣的事:Let constructorParent be superclass, proto be ObjectCreate(protoParent), and performing DefineMethod for constructor with arguments proto and constructorParent as the optional functionPrototype argument.
追溯 functionPrototype
變量的去向,發現是這樣的:
14.3.8 Runtime Semantics: DefineMethod[2.2]
With parameters object and optional parameter
functionPrototype
.
...6. Let closure be FunctionCreate(kind, StrictFormalParameters, FunctionBody, scope, strict). If
functionPrototype
was passed as a parameter then pass its value as thefunctionPrototype
optional argument of FunctionCreate....
9.2.5 FunctionCreate (kind, ParameterList, Body, Scope, Strict, prototype)[2.3]
The abstract operation FunctionCreate requires the arguments: kind which is one of (Normal, Method, Arrow), a parameter list production specified by ParameterList, a body production specified by Body, a Lexical Environment specified by Scope, a Boolean flag Strict, and optionally, an object
prototype
.
...4. Let F be FunctionAllocate(
prototype
, Strict, allocKind)....
9.2.3 FunctionAllocate (
functionPrototype
, strict [,functionKind] )[2.4]...
12. Set the
[[Prototype]]
internal slot of F tofunctionPrototype
....
原來 functionPrototype
被用做了 SubClass 的 [[Prototype]]
屬性!
Babel[2] 對繼承的實現以下:
function _inherits(subClass, superClass) { if (typeof superClass !** "function" && superClass !** null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
道理我都懂,但是爲何要這樣作?
要檢測一個對象是不是一個構造函數的實例,咱們一般會用 O instanceof C
這樣的表達式,在 spec 中,instanceof
運算符這樣被定義:
12.9.4 Runtime Semantics: InstanceofOperator(O, C)[2.5]
...
2. Let instOfHandler be GetMethod(C,@@hasInstance).
...
19.2.3.6 Function.prototype[@@hasInstance] ( V )[2.6]
Let F be the this value.
Return OrdinaryHasInstance(F, V).
7.3.19 OrdinaryHasInstance (C, O)[2.6]
...
4. Let P be Get(C, "prototype").
...
7. Repeat
Let O be O.[[GetPrototypeOf]]().
ReturnIfAbrupt(O).
If O is null, return false.
If SameValue(P, O) is true, return true.
9.1.1 [[GetPrototypeOf]] ( )[2.6]
Return the value of the [[Prototype]] internal slot of O.
大體描述以下:instanceof
運算符掉用了 Function.prototype
上的內部方法 @@hasInstance
,此方法將 this
對象(即 C)的 prototype
屬性與實例對象 O 的 [[prototype]]
對比,若是後者 [[prototype]]
爲 null
則返回 false
,若是二者相等,則返回 true
,不然沿原型鏈向上比較,直到得出結果。
便是:
O instanceof C => O.__proto__ === C.prototype ? true: O.__proto__.__proto__ === C.prototype ? true : ...
由此咱們能夠輕鬆僞造一個實例對象:
class A { whoami() { return 'Instance of A'; } } let a = new A(); let b = {}; Object.setPrototypeOf(b, A.prototype); // b.__proto__ = A.prototype a.whoami() =** b.whoami(); // true b instanceof A; // true
但是這是對對象的 __proto__
屬性的修改,和 SubClass.__proto__
有什麼關係?
少年,可別忘了 JavaScript 函數自己也是個對象喲!
在上面的代碼中,咱們使無關對象 b
的 __proto__
指向構造函數 A
的 prototype
,因而使 b
被斷定爲 A
的實例。同時,A
的全部原型方法都被 b
所繼承!
換句話說,若是將 SubClass
的 __proto__
屬性指向 SuperClass
,父類上的全部屬性都將被子類繼承!好比:
class A { static whoami() { return 'A Constructor!'; } greet() { return 'hello world!'; } } function B() {} Object.setPrototypeOf(B, A); B.whoami(); // 'A Constructor!'
此時,咱們再將 B.prototype
的 __proto__
屬性指向 A.prototype
,便可完成原型方法的繼承:
Object.setPrototypeOf(B.prototype, A.prototype); let b = new B(); b.greet(); // 'hello world!' b instanceof B; // true b instanceof A; // true
如此一來,子類就構造完成了!能夠開開心心造孩子去了!
利用 instanceof
運算符的定義,咱們還能玩出一些神奇的事,好比:
function A() {}; A.prototype = A; function B() {}; Object.setPrototypeOf(B, A); B instanceof A; // true!
(全文完)
重編自個人博客,原文地址:https://idiotwu.me/proto-property-and-es6-classes-inheritance/