关于 __proto__
属性,MDN 上的诠释是如许的[1]:
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.
等于说,__proto__
属性指向了实例对象的原型 Constructor.prototype
。那末,这个属性里隐藏着如何的黑魔法呢?
ES6 class 的完成
近来看 ECMAScript 6 的 spec,发现了一些有意思的东西,比方 class
章节:
14.5.14 Runtime Semantics: ClassDefinitionEvaluation[2.1]
With parameter className.
ClassTail : ClassHeritageopt { ClassBodyopt }
…6.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”).
ReturnIfAbrupt(protoParent).
If Type(protoParent) is neither Object nor Null, throw a TypeError exception.
Let constructorParent be superclass.
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;
}
原理我都懂,但是为何要如许做?
[[prototype]] 与原型链
要检测一个对象是不是是一个组织函数的实例,我们一般会用 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
如此一来,子类就组织完成了!能够开开心心造孩子去了!
恶搞:让函数 B 成为函数 A 的实例
应用 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/