7

TL;DR

I want to modify the prototype of a generator function instance--that is, the object returned from calling a function*.


Let's say I have a generator function:

function* thing(n){
    while(--n>=0) yield n;
}

Then, I make an instance of it:

let four = thing(4);

I want to define a prototype of generators called exhaust, like so:

four.exhaust(item => console.log(item));

which would produce:

3
2
1
0

I can hack it by doing this:

(function*(){})().constructor.prototype.exhaust = function(callback){
    let ret = this.next();
    while(!ret.done){
        callback(ret.value);
        ret = this.next();
    }
}

However, (function*(){})().constructor.prototype.exhaust seems very... hacky. There is no GeneratorFunction whose prototype I can readily edit... or is there? Is there a better way to do this?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Conor O'Brien
  • 987
  • 2
  • 16
  • 40
  • 1
    Why don't you just modify `thing.prototype`, isn't that enough? Extending builtins is quite a bad practice. – Bergi Jul 26 '16 at 02:14
  • @Bergi let's say I don't have `thing` defined, and I just want to extend the prototype. – Conor O'Brien Jul 26 '16 at 02:16
  • 1
    Btw, you should define `exhaust` as `for (const x of this) callback(x)` so that you get proper semantics by default – Bergi Jul 26 '16 at 02:27
  • @Bergi Did this change? Cause when I run `for (var item of ['a', 'b', 'c'].where(t => true)) { console.log( item ) }` I get 4 elements, where the last is `undefined` on chrome, from the statement you made, I wouldn't expect the `undefined` value. The `where` function is in this case the generator that just returns the matching values – Icepickle Sep 18 '17 at 10:51
  • @Icepickle I think your problem is [this](https://stackoverflow.com/questions/14633968/chrome-firefox-console-log-always-appends-a-line-saying-undefined) - the `undefined` value is not from your `console.log` but rather the result of the loop. Also what is `where`, did you mean `filter`? – Bergi Sep 18 '17 at 13:06
  • @Bergi Yeah, you are perfectly right :) Thanks :) Where is just a generator function for me, written like [so](https://jsfiddle.net/okvbndee/). You are perfectly right with your analysis :) – Icepickle Sep 18 '17 at 14:29

1 Answers1

7

There is no GeneratorFunction whose prototype I can readily edit... or is there?

No, GeneratorFunction and Generator do not have global names indeed.

If you want to modify them… Don't. Extending builtins is an antipattern. Write a utility module of static helper functions.

(function*(){})().constructor.prototype seems very... hacky. Is there a better way to do this?

I would recommend

const Generator = Object.getPrototypeOf(function* () {});
const GeneratorFunction = Generator.constructor;

then you can do

Generator.prototype.exhaust = function(…) { … };

if you really need to. But remember, if you just want to extend the generators created by function* thing then you can also do

thing.prototype.exhaust = …;

which is probably a better idea.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • So if `GeneratorFunction` produces `Generator` objects, then is there no `Generator` function? – Melab Sep 29 '21 at 17:19
  • @Melab There is. `GeneratorFunction` produces functions that are "subclasses" of `Generator` just like `Function` produces functions that are "subclasses" of `Object` (if used as constructors). The `Generator` class is the one that actually implements the `Iterator` protocol. – Bergi Sep 29 '21 at 18:14