An iterable provides Symbol.iterator returning an iterator. Next returns value and done. This protocol supports for-of, spread and collection constructors.
Step-by-step walkthrough
Step 1: Separate iterable from iterator
An iterable explains how to obtain a traversal; an iterator tracks one traversal’s progress. Symbol.iterator is the bridge. A next method alone does not necessarily make an object usable with for-of or spread.
Step 2: Define independent cursor state
If callers need repeatable traversal, each Symbol.iterator call should create fresh position state. An iterator that returns itself can be intentionally single-use. State the lifetime contract rather than assuming every iterable behaves like an array.
Step 3: Bound consumption
Lazy traversal lets consumers stop early, but spreading materializes all values. Infinite or very large sources need bounded iteration and a stopping condition. Resource-backed iterators also need a closure policy when a consumer abandons traversal.
Worked scenario
A range returns a fresh cursor for each traversal so two consumers can advance independently.
function range(start, end) {
return {
[Symbol.iterator]() {
let current = start;
return { next() {
return current < end
? {value: current++, done: false}
: {done: true};
}};
}
};
}
console.log([...range(2, 5)]); // [2, 3, 4]Walk through the example
The end is exclusive. Each traversal receives its own current variable, so the same range can be traversed twice independently. Returning done true marks completion; consumers should not treat that completion record as another ordinary value. The range itself does not eagerly allocate the numbers.
Common mistake
An iterator can be single-use. Spreading an infinite iterable never completes, and large spreads lose incremental processing.
Verify the behavior
Test an empty interval, one value and two concurrent iterators. Check that advancing one iterator does not advance the other. Define how reversed bounds should behave, and avoid spreading a source that may be infinite.
Interview exercise
Make an object work with for-of.
Answer and reasoning
Provide Symbol.iterator and a valid next result; defining next alone does not necessarily make the object iterable.
Follow-up discussion
Why use an iterator instead of an array? It can deliver values incrementally and avoid storing an entire generated sequence. Does laziness guarantee low memory? No: consumers can still buffer every value, and the iterator may retain substantial source state.
Continue learning
Compare the scenario with the JavaScript interview questions and test your understanding with the JavaScript MCQs. For terminology and implementation details, consult the reference material.