JavaScript
Custom behavior with Symbol.iterator
Learn to add custom iteration behavior to an object with Symbol.iterator.
Custom behavior with Symbol.iterator
To make an object usable with for...of, you define the [Symbol.iterator] method. This implements the iteration protocol: the object returns an iterator, and the iterator yields { value, done } at each step.
This is a metaprogramming tool: structures like for...of, spread and Array.from call this method indirectly. You write the method, the engine does the rest.
A range, a word list or a record set can then be traversed like an ordinary array.
The iteration protocol
The iteration protocol has two parts. An iterable is an object with a [Symbol.iterator] method. An iterator is an object with a next() method that returns { value, done } on each call.
A typical pattern: [Symbol.iterator]() { let i = ...; return { next() { ... } }; }. The state i is kept in a closure; each next() call advances it.
When done: true is produced, the loop ends. Calls after that should keep returning the same final result.
Exercise Make a range iterable
Define a range object with from: 1 and to: 3 properties. Add a [Symbol.iterator]() method: initialize let current = this.from, take const last = this.to and return a next() method. next returns { value: current++, done: false } when current <= last, otherwise { value: undefined, done: true }. Finally loop with for (const n of range), push each value to collected and console.log(collected).
Show example solution
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 };
}
return { value: undefined, done: true };
}
};
}
};
const collected = [];
for (const n of range) {
collected.push(n);
}
console.log(collected);
Test yourself
-
What should [Symbol.iterator] return?
- A number
- An array
- A string
- An iterator object with a next method
Show answer
An iterator object with a next method — Correct! [Symbol.iterator] must return an iterator with a next() method.
-
Complete the iterator
const range = {
[Symbol.iterator]() {
let current = 1;
return {
____() {
return { value: current++, done: false };
}
};
}
};
Show answer
const range = {
[Symbol.iterator]() {
let current = 1;
return {
next() {
return { value: current++, done: false };
}
};
}
};
-
Match each concept to its role
- next()
- Produces the next value
- done
- Ends the loop
- value
- Step's value
- Symbol.iterator
- Makes iterable
Next lesson: The decorator pattern