Symbol.iterator

Marks the method that makes an object work with for...of, spread and destructuring.

Signature
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

Try it yourself
Loading editor…
Press Run to execute the code.

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.

Example · javascript
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.

Example · javascript
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

  • Be the first to comment on this lesson.