To make an object loopable, give it a [Symbol.iterator]() method that returns an iterator.
A range of numbers
const range = {
from: 1,
to: 5,
[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 };
}
};
}
};
for (const n of range) {
console.log(n); // 1, 2, 3, 4, 5
}
console.log([...range]); // → [1, 2, 3, 4, 5]
console.log(Math.max(...range)); // → 5
Because range follows the protocol, for...of, spread, destructuring and Array.from all work with it.
In a class
class Playlist {
#songs = [];
add(song) {
this.#songs.push(song);
return this;
}
[Symbol.iterator]() {
let index = 0;
const songs = this.#songs;
return {
next: () =>
index < songs.length
? { value: songs[index++], done: false }
: { value: undefined, done: true }
};
}
}
const list = new Playlist().add("Song A").add("Song B");
for (const song of list) {
console.log(song);
}
console.log([...list].length); // → 2
The playlist hides its array (private field) but lets other code loop over it.
A simpler way: return an array’s iterator
class Team {
#members = ["Maya", "Arjun"];
[Symbol.iterator]() {
return this.#members[Symbol.iterator](); // reuse the array's iterator
}
}
console.log([...new Team()]); // → ["Maya", "Arjun"]
Even simpler: a generator
A generator function makes the iterator for you. You will see it in the next lesson: Generators.
class Team {
#members = ["Maya", "Arjun"];
*[Symbol.iterator]() {
yield* this.#members;
}
}
console.log([...new Team()]); // → ["Maya", "Arjun"]
Iterable or iterator?
- An iterable can produce a new iterator each time, so you can loop over it many times (arrays, ranges).
- An iterator is a one-time cursor. Once it reaches the end, it is finished.
Stopping early
If a loop exits early (break, return, an error), JavaScript calls the iterator’s optional return() method so it can clean up, such as closing a file.
Do you need this?
map/filter are enough. Custom iterables are useful when the data is large, infinite or computed on demand, or when you want to hide how a collection is stored.