21.7 基于迭代 API 的继承关系(包含生成器)

下面的图展示了在 ECMAScript 6 中各种对象之间的关系(此图基于 ECMAScript 规范中 Allen Wirf-Brock 的图):

说明:

  • 空心箭头表示两个对象的继承关系。换句话说,从 x 指向 y 的箭头意味着 Object.getPrototypeOf(x) === y
  • 圆括号表示当前被包起来的对象是存在的,但是不能通过全局变量来访问。
  • 带有 instanceof 字眼的箭头如果从 x 指向 y ,就表明 x instanceof y
    • o instanceof C 实际上就相当于 C.prototype.isPrototypeOf(o)
  • 带有 prototype 字眼的箭头如果从 x 指向 y ,就表明 x.prototype === y

此图揭示了两个有趣的事实:

第一个,生成器函数 g 很像构造函数(甚至可以通过 new 来调用它,这和直接调用的效果是一样的):它创建的生成器对象是它的实例,添加到 g.prototype 上的方法成为原型方法,等等:

> function* g() {}
> g.prototype.hello = function () { return 'hi!'};
> let obj = g();
> obj instanceof g
true
> obj.hello()
'hi!'

第二个,如果你想给所有的生成器对象添加方法,最好将这些方法添加到 (Generator.object) 上。访问这个对象的一种方式如下:

> let Generator_prototype = Object.getPrototypeOf(function* () {}).prototype;
> Generator_prototype.hello = function () { return 'hi!'};
> let generatorObject = (function* () {})();
> generatorObject.hello()
'hi!'

21.7.1 IteratorPrototype

在图中没有 (Iterator) ,因为不存在这样的对象。但是,考虑到 instanceof 的工作原理,同时也因为 (IteratorPrototype)g1() 的原型,你仍然可以说 g1()Iterator 的实例。

ES6 中所有的迭代器在原型链中都有 (IteratorPrototype) 。这个对象是可迭代的,因为它有下面的方法。因此,所有的 ES6 迭代器都是可迭代的(因此,你可以在这些对象上应用 for-of )。

[Symbol.iterator]() {
    return this;
}

规范推荐使用如下代码访问 (IteratorPrototype)

const proto = Object.getPrototypeOf.bind(Object);
let IteratorPrototype = proto(proto([][Symbol.iterator]()));

也可以使用:

let IteratorPrototype = proto(proto(function* () {}.prototype));

引用 ECMAScript 6 规范中的话:

ECMAScript 代码可能定义继承自 IteratorPrototype 的对象。IteratorPrototype 对象提供了一个给所有可迭代对象添加 额外方法的地方。

在后续的 ECMAScript 版本中,也许可以直接获取到 IteratorPrototype ,并且包含一些工具方法,比如 map()filter()来源) 。

21.7.2 生成器中的 this

一个生成器函数混合了两方面的内容:

  • 1、它是一个设置和返回生成器对象的函数。
  • 2、它包含了生成器对象执行过程的代码。

这就是为什么生成器函数中的 this 值不太好猜测的原因。

在函数调用和方法调用中, this 值和其作为普通函数(非生成器函数)的时候一致:

function* gen() {
    'use strict'; // just in case
    yield this;
}

// Retrieve the yielded value via destructuring
let [functionThis] = gen();
console.log(functionThis); // undefined

let obj = { method: gen };
let [methodThis] = obj.method();
console.log(methodThis === obj); // true

如果通过 new 调用一个生成器函数,那么在函数中访问 this 将会得到一个 ReferenceError 错误(源自 ES6 规范):

function* gen() {
    console.log(this); // ReferenceError
}
new gen();

有一个变通的方法,就是把生成器函数包裹在一个普通函数里面,通过 next() 传入生成器自身的生成器对象。这意味着生成器必须使用第一个 yield 拿到自身的生成器对象:

let generatorObject = yield;

results matching ""

    No results matching ""