__proto__ 属性与 ES6 classes 的继续

关于 __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)
  1. If superclass has a [[FunctionKind]] internal slot whose value is “generator”, throw a TypeError exception.

  2. Let protoParent be Get(superclass, “prototype”).

  3. ReturnIfAbrupt(protoParent).

  4. If Type(protoParent) is neither Object nor Null, throw a TypeError exception.

  5. 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 the functionPrototype 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 to functionPrototype.

本来 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;
}

原理我都懂,但是为何要如许做?

《__proto__ 属性与 ES6 classes 的继续》

[[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]

  1. Let F be the this value.

  2. Return OrdinaryHasInstance(F, V).

7.3.19 OrdinaryHasInstance (C, O)[2.6]

4. Let P be Get(C, “prototype”).

7. Repeat
  1. Let O be O.[[GetPrototypeOf]]().

  2. ReturnIfAbrupt(O).

  3. If O is null, return false.

  4. If SameValue(P, O) is true, return true.

9.1.1 [[GetPrototypeOf]] ( )[2.6]

  1. 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__ 指向组织函数 Aprototype,因而使 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!

《__proto__ 属性与 ES6 classes 的继续》

(全文完)

参考资料

  1. Object.prototype.__proto__ – JavaScript | MDN

  2. ECMAScript 2015 Language Specification

  3. Babel · The compiler for writing next generation JavaScript

重编自我的博客,原文地点:https://idiotwu.me/proto-property-and-es6-classes-inheritance/

    原文作者:Dolphin_Wood
    原文地址: https://segmentfault.com/a/1190000003826981
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞