Spreading an iterable ...obj[Symbol.iterator](...)

Viewed 136

I understand most of the following code, but I'm completely stuck on [...numbers[Symbol.iterator].

var numbers = {
    *[Symbol.iterator]({
        start = 0,
        stop = 100,
        step = 1
    } = {}) {
        for(let x = start; x <= stop; x+=step) {
            yield x;
        }
    }
};

// should print 0.. 100 in steps of 1
for(let num of numbers) {
    console.log(num);
}

// should print 6..30 in steps of 4
console.log(`My lucky numbers are: ${
    [...numbers[Symbol.iterator]({
        start: 6,
        stop: 30,
        step: 4
    })]}`
);

In my mind:

If it was just [...numbers] it would spread the numbers 0 - 100 because the object has an iterator function/generator. If it was just numbers[Symbol.iterator]() it would look up and return the generator.

But I've no clue with [...numbers[Symbol.iterator](...), which looks like a strange combination of the two.

I'm struggling to get results when googling this syntax. If anyone could explain it like I'm 5 that would be much appreciated!

2 Answers

This is really weird usage of Symbol.iterator, you normally shouldn't give them parameters. You can pull it apart into

const generatorFunction = numbers[Symbol.iterator];
const iterator = generatorFunction({
    start: 6,
    stop: 30,
    step: 4
});
const array = [...iterator];
console.log(`My lucky numbers are: ${array}`);

Notice that iterator is iterable, it inherits a [Symbol.iterator] method returning itself, which allows it to be used in the array spread element. That the generator method (which is called explicitly here) is named [Symbol.iterator] as well doesn't matter, it only leads to confusing. A more better style to write this would be

const numbers = {
    *range(start, stop, step=1) {
        for(let x = start; x <= stop; x+=step) {
            yield x;
        }
    },
    [Symbol.iterator]() {
        return this.range(0, 100);
    },
};

console.log([...numbers.range(6, 30, 4)]);

I didn't originally understand the first half of Bergi's answer

It became clear (for my brain at least) when viewed as leaving the spread operator until last, which to be fair is exactly what he explained, but I couldn't see it.

Anyway:

[numbers[Symbol.iterator]({...})] returns the generator, which is then spread.

Related