JavaScript Symbol.iterator Explained
In JavaScript, Symbol.iterator is a built-in well-known
symbol that specifies the default iteration behavior for an object. This
article covers the fundamental purpose of Symbol.iterator,
the mechanics of the JavaScript Iteration Protocols, the built-in
language features that rely on it, and how to implement custom iteration
logic in your own objects.
The Role of Symbol.iterator
The primary purpose of Symbol.iterator is to make an
object iterable. It serves as the key for a method that returns an
iterator object conforming to the Iterator Protocol. When an object
defines this method, it signals to JavaScript engines and APIs that its
values can be sequentially accessed.
Under the hood, an iterator returned by
[Symbol.iterator]() must implement a next()
method. This next() method must return an object with two
properties: - value: The current item in the sequence. -
done: A boolean indicating whether the iteration sequence
is complete (true) or ongoing (false).
Built-In Iterables
JavaScript includes several built-in data structures that have
Symbol.iterator implemented on their prototypes by
default:
- Arrays and TypedArrays
- Strings (iterates over Unicode code points)
- Maps (iterates over
[key, value]pairs) - Sets (iterates over distinct elements)
- The
argumentsobject and DOM collections likeNodeList
Standard objects ({}) do not have a default
Symbol.iterator method because there is no universal way to
determine the iteration order or what data (keys, values, or entries)
should be returned.
Language Features Powered by Symbol.iterator
When an object has a valid Symbol.iterator method, it
automatically gains compatibility with numerous JavaScript operators and
constructs, including:
for...ofLoops: Iterates directly over the values provided by the iterator.- Spread Operator (
...): Expands iterable elements into function arguments or array literals. - Array Destructuring: Extracts values into distinct
variables based on iterator position (e.g.,
const [first, second] = myIterable;). Array.from()andPromise.all(): Consumes iterables to create arrays or handle collections of promises.yield*Expression: Delegates to another generator or iterable inside a generator function.
Implementing a Custom Iterator
You can make any custom object iterable by defining a
[Symbol.iterator] method on it.
Standard Iterator Pattern
const range = {
from: 1,
to: 3,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
if (current <= last) {
return { value: current++, done: false };
} else {
return { done: true };
}
}
};
}
};
for (const num of range) {
console.log(num); // Outputs: 1, 2, 3
}
console.log([...range]); // Outputs: [1, 2, 3]Generator Function Pattern
Because generator functions return generator objects that naturally conform to the iterator protocol, you can simplify custom iteration by using a generator method:
const team = {
lead: 'Alice',
tester: 'Bob',
developer: 'Charlie',
*[Symbol.iterator]() {
yield this.lead;
yield this.tester;
yield this.developer;
}
};
for (const member of team) {
console.log(member); // Outputs: Alice, Bob, Charlie
}Summary
Symbol.iterator establishes a standardized interface for
sequential data access across the JavaScript language. By implementing
this well-known symbol, you ensure that custom data structures
seamlessly integrate with modern syntax like for...of
loops, destructuring, and spread operations.