Iterators & Generators
Something Has Been Hidden From You
Every time you wrote for...of, spread an array, or destructured a value, something was running underneath that you never saw. All three use the same protocol — the iterator protocol.
Start by seeing that arrays, strings, and maps all share something:
Symbol.iterator is a special key. If an object has it, JavaScript considers it iterable and lets you use for...of, spread, and destructuring on it. If it doesn't, you get a TypeError.
The Iterator Protocol
An iterator is any object with a next() method that returns { value, done }. Let's build one by hand:
That is the entire protocol. next() returns the next value and whether the sequence is exhausted. for...of calls next() in a loop until done is true. Spread does the same thing. Destructuring does the same thing. One protocol, many consumers.
Making Your Own Iterable
To make an object work with for...of, give it a [Symbol.iterator] method that returns an iterator:
One method added — and suddenly your custom class works with every language feature that understands the protocol. This is why the protocol exists: define it once, plug into everything.
Generators — Functions That Pause
Writing iterators by hand is verbose. Generators are a special function syntax that produces iterators automatically — and they can pause mid-execution and resume later:
yield is a pause point. The generator runs until it hits yield, hands the value out, and freezes — all local variables, the exact position in the code, everything preserved. The next next() call resumes from exactly where it stopped.
Calling count() returns a generator object. The body does not run at all until you call next().
Generators as Iterables
A generator object is automatically iterable — you can drop it straight into for...of:
Compare this to the Range class above — same behaviour, a fraction of the code. Generators earn their syntax.
Infinite Sequences
Generators can yield infinitely — because they only compute the next value when asked. They never materialise the whole sequence:
A lazy infinite sequence. No memory wasted on values you'll never use. This is a fundamentally different model from arrays — you pull values one at a time rather than generating all of them upfront.
Passing Values Back Into a Generator
yield is a two-way door. The value you pass to next(value) becomes the result of the yield expression inside the generator:
This two-way communication is what makes generators powerful for stateful workflows — each yield is a checkpoint where the outside world can both receive a value and send one back.
Generator Composition with yield*
yield* delegates to another iterable — flattening it into the current generator:
What You Have Learned
Iterators and generators are the machinery underneath for...of, spread, and destructuring — now visible.
The key ideas:
- The iterator protocol: any object with
next()returning{ value, done }is an iterator - The iterable protocol: any object with
[Symbol.iterator]()returning an iterator works withfor...of, spread, and destructuring - Generators (
function*) create iterators automatically —yieldpauses,next()resumes - Generators remember their entire local state between calls — position, variables, everything
- Infinite generators are safe — they compute values lazily, only when pulled
next(value)passes a value into the generator —yieldis a two-way channelyield*delegates to another iterable — composing sequences without flattening by hand
In the next lesson: Symbols, WeakMap & WeakRef — JavaScript's more obscure but powerful primitives. Symbols give you truly unique keys that never collide. WeakMap lets you attach private data to objects without preventing garbage collection. The tools for writing libraries and frameworks that don't leak memory.