JavaScript Iterators and Iterables
The iterator and iterable protocols define how JavaScript values produce sequences consumed by constructs such as `for...of`. An iterable supplies an iterator, while an iterator produces successive values through `next()` until it reports completion.
An iterable is an object that implements the well-known symbol `Symbol.iterator` as a method. Calling that method returns an iterator object. Built-in iterables include arrays, strings, maps, and sets.
An iterator implements a `next()` method. Each call returns an object with a `value` property and a boolean `done` property. Iteration finishes when `done` is `true`.
The `for...of` statement obtains an iterator from the value and repeatedly calls `next()`, using each `value` until completion. This is why `for...of` works with arrays and strings but does not generally work with ordinary plain objects.
The two protocols are related but distinct: an iterable knows how to create an iterator, whereas an iterator knows how to advance through a sequence. An object can implement both protocols, as many custom iterators do.
JavaScript also uses these protocols in other language features, including spread syntax, array destructuring, and `Array.from()`. Generator functions are a convenient way to create objects that implement the iterator and iterable protocols.
Key points
- Iterable objects expose `Symbol.iterator` and produce iterators.
- Iterators expose `next()`, which returns `{ value, done }`.
- `for...of` consumes iterables rather than requiring arrays specifically.
- An iterator can also be iterable when its `Symbol.iterator` method returns itself.
- The protocols enable custom sequence-like objects and are used by spread, destructuring, and `Array.from()`.
Interview questions
What does an iterator's `next()` method return?
`next()` returns an iterator result object. Before completion, it normally has a `value` containing the current result and `done: false`. When the sequence is exhausted, the iterator returns an object with `done: true`; the value is then generally ignored by consumers such as `for...of`. This protocol lets consumers advance without knowing how the sequence is implemented.
What is the difference between an iterable and an iterator in JavaScript?
An iterable is a value that implements `Symbol.iterator` and can produce an iterator. An iterator implements `next()` and returns iteration-result objects containing `value` and `done`. `for...of` first obtains an iterator from the iterable and then consumes it. Some objects, including generator results, implement both protocols.
Why does `for...of` work on arrays but not ordinary objects?
Arrays implement the iterable protocol through `Symbol.iterator`, so `for...of` can obtain an iterator from them. A typical object literal does not provide `Symbol.iterator` by default. `for...in` is designed for enumerable property keys and therefore works differently. An object can become usable with `for...of` if it implements the iterable protocol.
How would you create a custom iterable without using a generator?
Define a `Symbol.iterator` method that returns an object implementing `next()`. The iterator maintains whatever internal state is needed to determine the next result. Each call to `next()` returns `{ value, done: false }` until the sequence ends, after which it returns `{ done: true }`. This separates the consumer-facing iteration mechanism from the underlying data representation.
Which JavaScript features consume the iterable protocol?
`for...of` directly consumes iterables by acquiring their iterator and repeatedly calling `next()`. Spread syntax, array destructuring, and `Array.from()` also rely on iteration semantics for iterable inputs. This means custom iterables can integrate with several language features without being arrays. The key requirement is implementing the expected iterable protocol correctly.
Why might an iterator implement `Symbol.iterator` by returning itself?
An iterator that returns itself from `Symbol.iterator` satisfies both protocols. This allows the iterator itself to be passed to constructs that expect an iterable, while its `next()` method supplies the sequence. Generator objects follow this general pattern. It is particularly useful for stateful iterators whose traversal represents one ongoing sequence.