Symbol.iterator
Marks the method that makes an object work with for...of, spread and destructuring.
Symbol.iterator
// as implemented on an object:
[Symbol.iterator]()- Added in JavaScript ES2015
Return value
symbolThe well-known symbol used as the property key for an object's default iterator method. The method itself must return an iterator object.
Errors
Consumers throw a TypeError if the property is missing or not callable ("is not iterable"), or if the method returns a non-object.
Symbol.iterator is the key every iteration construct looks up. for...of, array spread, array and object destructuring of arrays, Array.from(), Promise.all(), the Map and Set constructors and yield* all call obj[Symbol.iterator]() and then drive the returned iterator's next().
When to reach for it
Implement it on any class that represents a sequence — a tree, a linked list, a paged collection, a numeric range — and every one of those constructs starts working for free. The easiest correct implementation is a generator method: *[Symbol.iterator]() { … }, because generators already satisfy the iterator protocol and are themselves iterable.
What trips people up
The method must return an object with a next(); returning a plain value gives "[Symbol.iterator]() returned a non-object value". Reusability is the other trap: if you return the same iterator instance every time, a second for...of over the object yields nothing, because the first loop exhausted it. Return a fresh iterator on each call. Note also that a plain object literal is not iterable — [...{a: 1}] throws — while strings, arrays, Map, Set, arguments and typed arrays all ship with this method.
Example
When to use it
- Let a custom collection class be spread into an array or looped with for...of
- Expose a lazy range or infinite sequence that materialises values on demand
- Make a tree or graph node iterable in traversal order so destructuring works
Frequently asked questions
Why do I get "x is not iterable"?
Because the value has no callable [Symbol.iterator] method — plain objects are the usual culprit. Add a *[Symbol.iterator]() generator method, or iterate Object.entries(obj) instead.
How do I make a class work with for...of?
Define a generator method keyed by Symbol.iterator: *[Symbol.iterator]() { yield ... }. That single method also enables spread, Array.from(), destructuring and yield*.
Why does my object only iterate once?
Because [Symbol.iterator]() returns the same iterator each time instead of a new one. Create the iterator state inside the method so each call starts a fresh traversal.
More examples
A hand-written iterator object
Because a fresh closure over n is created on each [Symbol.iterator]() call, the spread and the loop each get a full sequence rather than sharing one exhausted iterator.
const countdown = {
from: 3,
[Symbol.iterator]() {
let n = this.from;
return {
next: () => (n > 0 ? { value: n--, done: false } : { value: undefined, done: true }),
};
},
};
console.log([...countdown]);
for (const n of countdown) console.log("tick", n);What a missing or malformed iterator looks like
Both failures are TypeErrors: the plain object has no Symbol.iterator at all, and broken returns a number instead of an iterator object. Strings and Maps have the method built in.
try {
[...{ a: 1 }];
} catch (err) {
console.log(err.constructor.name, "- plain object");
}
const broken = { [Symbol.iterator]: () => 1 };
try {
[...broken];
} catch (err) {
console.log(err.constructor.name, "- non-object iterator");
}
console.log(typeof "hi"[Symbol.iterator]);
console.log(typeof new Map()[Symbol.iterator]);
Discussion