简体   繁体   中英

How to extend the Generator class?

I tried to filter a generator and had the expectation that this kind of general functionality must be defined anywhere in JavaScript, because it is defined for Arrays, but I can not find it. So I tried to define it. But I can not extend the built-in generators.

I have an example generator

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

generating some numbers.

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

If I build an array, I can filter the array by the use of the filter function for arrays.

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

But I do not want to build an array. Instead I want to take the old generator and build a new filtering generator. For this I wrote the following function.

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

which can be used to do what I want.

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

But this is a very general function, which can be applied to every generator in the same way the filter function can be applied to every Array . So I tried to extend generators in general, but I do not understand how.

The MDN documentation for generators suggests somehow that Generator.prototype may exist, but it does not seem to exist. When I try to define something in Generator.prototype , I get the error

ReferenceError: Generator is not defined

How can I extend the built-in Generator class?

With the traditional caveat that extending built-in prototypes is not necessarily the best idea, and that it's something to be done with caution, you can get the generator function prototype with

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

With that you could add a filter() capability:

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

And thus:

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

will print [0, 2, 4, 6, 8] .

To be clear: this answer is about extending the prototype for generator functions , not the generator objects themselves. Thus this will ensure that every generator function in the program can use that .filter() method and any other similar extension.

It turned out that I was confused about Generator and Iterable . I thought I had to extend Generator , but it is actually sufficient to extend Iterable . And the fact that Iterable does not seem to be defined by JavaScript either, makes it easy to define it, to avoid problems with modifications of built-in prototypes.

This actually does what I tried to achieve.

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;
  });
};

I can create Iterables

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

can use them

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

and can filter them

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM