const range = {
data: [1, 2, 3],
index: 0,
[Symbol.iterator]() {
return {
next: () => {
if (this.index < this.data.length) {
return { value: this.data[this.index++], done: false };
}
return { value: undefined, done: true };
}
};
}
};
for (const n of range) console.log(n);
for (const n of range) console.log(n);const range = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
const data = this.data;
return {
next() {
if (index < data.length) {
return { value: data[index++], done: false };
}
return { value: undefined, done: true };
}
};
}
};
for (const n of range) console.log(n);
for (const n of range) console.log(n);Bug: index is stored on the range object itself — shared across all iterations. After the first loop, index = 3. The second loop starts with index = 3 and immediately gets done: true.
Explanation: index is now a local variable in the [Symbol.iterator]() call — a fresh closure per iteration. Each for...of gets a brand-new independent iterator.
Key Insight: An iterable must return a FRESH iterator each time [Symbol.iterator]() is called. Never store iterator state on the iterable itself.