I’ve seen a few iterators implemented in javascript, and every time it seems like they’re off by one (there’s obviously something I’m missing) Here’s an example
const numbers = [1, 2, 3];
numbers[Symbol.iterator] = function() {
let idx = 0;
return {
next: function() {
return {
value: numbers[idx++],
done: idx > numbers.length,
};
},
};
};
// Run the custom iterator:
for (const num of numbers) console.log(num);
In this example, done is set to true when idx > numbers.length. However doesn’t this mean that numbers[3] will not be set as done = true (even though there’s no value for numbers[3]? I don’t fully understand why this code works.
Would appreciate any help — thanks!
>Solution :
There are two things to realise:
-
donedoes not indicate whether the returned value is the last one, but whether the iterator has no more value to return. As Mozilla contributors write:doneA boolean that’s false if the iterator was able to produce the next value in the sequence.
-
idx++evaluates and then increments, and so the expression for thedoneproperty works with anidxvalue that is one more than was used for setting thevalueproperty.
So we have these objects returned:
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: undefined, done: true }
Alternative code without ++ side-effect
To eliminate the side effect of ++ mentioned in point 2, it maybe helps to look at this version, which produces the exact same {value,done} objects:
const numbers = [1, 2, 3];
numbers[Symbol.iterator] = function() {
let idx = -1; // <--- one less, so we can first increment
return {
next: function() {
idx++; // First increment
return {
value: numbers[idx], // then use
done: idx >= numbers.length, // ...as expected
};
},
};
};
// Run the custom iterator:
for (const num of numbers) console.log(num);