Search code examples
javascriptiteratorgenerator

Can I attach a property to a generator created with function*() in JavaScript?


I am looking for a way to expose additional properties for a generator constructed using function*(). I tried two naive approaches, neither of which accomplishes what I want.

Approach 1 illustrates what I am trying to do, naively trying to use this to attach a property to the generator:

function* counter(startValue) {

  // trying to expose a property through "this", 
  // which does not work as intended 
  // (I actually expected this, but it illustrates what I am trying to do)
  this.startValue = startValue;

  // simple counter
  let currentValue = startValue;
  while (true) yield currentValue++;

}

// user code

let myCounter = counter(10);

console.log(myCounter.next().value);
// -> 10

console.log(myCounter.next().value);
// -> 11

// I want myCounter.startValue to expose the start value (10)
// unfortunately this does not work
console.log(myCounter.startValue);
// -> undefined

Approach 2, attempt to use a closure to store the start value:

// use a closure to store configuration & state
function counter(startValue) {
    let currentValue = startValue;
    let gen = function*() {
        while(true) yield currentValue++;
    }
    // Again, I want the generator to expose the "startValue" parameter
    // This also does not work:
    gen.startValue = startValue;
    return gen;
}

// user code

let myCounter = counter(10)();

myCounter.next().value;
// -> 10

myCounter.next().value;
// -> 11

// Again, no luck accessing the start value
myCounter.startValue;
// -> undefined

I guess since the actual generator object is constructed implicitly by the JS runtime, there is no way to attach additional properties to it without creating some kind of wrapper object?

(For reasons of the overall project structure, constructing the generator and afterwards attaching the property (myCounter.startValue = 10 somewhere in the user code) is not an option for me, it has to be done in the constructor function)


Solution

  • Your closure attempt was the right approach, you just missed to create the generator (to which you want to attach the property) inside the function, instead of attaching it to the generator function:

    function* count(currentValue) {
        while(true) yield currentValue++;
    }
    function counter(startValue) {
        const gen = count(startValue);
        gen.startValue = startValue;
        return gen;
    }
    
    let myCounter = counter(10);
    
    myCounter.next().value; // -> 10
    myCounter.next().value; // -> 11
    
    myCounter.startValue; // -> 10