【问题标题】:How to extend the Generator class?如何扩展生成器类?
【发布时间】:2018-05-12 01:30:03
【问题描述】:

我试图过滤一个生成器,并期望这种通用功能必须在 JavaScript 中的任何地方定义,因为它是为数组定义的,但我找不到它。所以我试图定义它。但是我不能扩展内置的生成器。

我有一个示例生成器

function make_nums ()
{
  let nums = {};
  nums[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
  };
  return nums;
}

生成一些数字。

[...make_nums()] // => Array [ 1, 2, 3 ]

如果我构建一个数组,我可以使用数组的filter 函数来过滤数组。

[...make_nums()].filter(n => n > 1) // => Array [ 2, 3 ]

但我不想建立一个数组。相反,我想使用旧的生成器并构建一个新的过滤生成器。为此,我编写了以下函数。

function filtered (generator, filter)
{
  let g = {};
  g[Symbol.iterator] = function* () {
    for (let value of generator)
      if (filter(value))
        yield value;
  };
  return g;
}

可以用来做我想做的事。

[...filtered (make_nums(), n => n > 1)] // => Array [ 2, 3 ]

但这是一个非常通用的函数,它可以应用于每个生成器,就像filter 函数可以应用于每个Array 一样。所以我尝试扩展生成器,但我不明白如何。

generators 的 MDN 文档以某种方式暗示 Generator.prototype 可能存在,但它似乎不存在。当我尝试在 Generator.prototype 中定义某些内容时,我得到了错误

ReferenceError: 生成器未定义

如何扩展内置的Generator 类?

【问题讨论】:

  • "但这是一个非常通用的函数" - 有什么问题?
  • 既然可以扩展all iterators,为什么还要扩展Generators?

标签: javascript ecmascript-6 generator


【解决方案1】:

原来我对GeneratorIterable 感到困惑。我以为我必须扩展Generator,但实际上扩展Iterable 就足够了。而且Iterable 似乎也没有被 JavaScript 定义,这使得定义它很容易,避免了修改内置原型的问题。

这实际上做了我试图实现的目标。

class Iterable {
  constructor (generator) {
    this[Symbol.iterator] = generator;
  }
}

Iterable.prototype.filter = function (predicate) {
  let iterable = this;
  return new Iterable (function* () {
    for (let value of iterable)
      if (predicate (value))
        yield value;
  });
};

我可以创建Iterables

make_nums = new Iterable(function* () { yield 1; yield 2; yield 3; });

可以使用

[...make_nums] // => Array [ 1, 2, 3 ]

并且可以过滤它们

[...make_nums.filter(n => n > 1)] // => Array [ 2, 3 ]

【讨论】:

    【解决方案2】:

    根据传统的警告,扩展内置原型不一定是最好的主意,并且需要谨慎行事,您可以使用生成器函数原型获得

    const genproto = Object.getPrototypeOf(function*(){});
    

    您可以添加filter() 功能:

    Object.defineProperty(genproto, "filter", {
      value: function*(predicate) {
        for (let value of this())
          if (predicate(value)) yield value;
      }
    });
    

    因此:

    console.log([... function*() {
        for (let i = 0; i < 10; i++) yield i;
      }.filter(value => value % 2 === 0)
    ]);
    

    将打印[0, 2, 4, 6, 8]

    要明确:这个答案是关于扩展生成器函数的原型,而不是生成器对象本身。因此,这将确保程序中的每个生成器函数都可以使用 .filter() 方法和任何其他类似的扩展。

    【讨论】:

    • OP 想要扩展 Generator,而不是 GeneratorFunction
    • @Bergi 好吧,我不知道如何理解这一点;但是,您可能是对的。这是我从未想过的事情,但是一旦我原谅自己扩展了该原型对象,它似乎确实很有用。 .take() 之类的东西真的很容易实现。
    • 只需将getPrototypeOf(function*(){}) 更改为getPrototypeOf(function*(){}()) 并将this() 更改为this
    • @Bergi 很确定,但我的意思是生成器对象本身不一定继承自该原型;可迭代/迭代器协议就是:协议。因此不可能知道给定的生成器对象将具有扩展功能。但也许我错过了一些东西;这让我有点头疼。
    • This diagram 可能会有所帮助(可能既要澄清,又会让你的头更痛:P)。所有生成器对象(通过调用生成器函数创建)都继承自 GeneratorPrototypeIteratorPrototype
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-15
    • 2019-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多