JavaScript
The iterable protocol
Learn how an object becomes iterable with Symbol.iterator.
The iterable protocol
To make an object iterable, you define a [Symbol.iterator] method. This method returns an iterator; an object with a next() method. When JavaScript sees for...of, it first calls this method, then pulls values with next() each step.
This contract is the iterable protocol. Arrays, strings, Map and Set already have it. You can add it to your own objects so they work with for...of, spread (...) and destructuring.
The iterator factory
[Symbol.iterator] works like a factory: each call returns a fresh iterator. That way the same iterable can be traversed multiple times independently. Inside, you keep a state variable with let and advance one step on each next() call.
When the iterator is exhausted, it returns { value: undefined, done: true }. for...of stops there. The power of the factory pattern: each loop starts with its own state and is not affected by the previous loop.
Exercise Make the list iterable
Create an object called list with an items property holding a three-element array. Add a [Symbol.iterator] method to the object. It should return an iterator: start with let i = 0, and on each next() call return the next item as { value, done: false }; when the array ends return { value: undefined, done: true }. Then loop over the items with for...of and log each one with console.log.
Show example solution
const list = {
items: ["apple", "pear", "cherry"],
[Symbol.iterator]() {
let i = 0;
const items = this.items;
return {
next() {
if (i < items.length) {
return { value: items[i++], done: false };
}
return { value: undefined, done: true };
}
};
}
};
for (const item of list) {
console.log(item);
}
Test yourself
-
Which method must you define to make an object iterable?
- [Symbol.iterator]
- [Symbol.iterator()]
- iterator()
- Symbol()
Show answer
[Symbol.iterator] — Correct. The [Symbol.iterator] method makes an object iterable.
-
Add the iterable method
const list = {
items: ["a", "b"],
____() {
let i = 0;
const items = this.items;
return {
next() {
return items.length > i
? { value: items[i++], done: false }
: { value: undefined, done: true };
}
};
}
};
Show answer
const list = {
items: ["a", "b"],
[Symbol.iterator]() {
let i = 0;
const items = this.items;
return {
next() {
return items.length > i
? { value: items[i++], done: false }
: { value: undefined, done: true };
}
};
}
};
-
Iterable protocol concepts
- Symbol.iterator
- Method that produces iterators
- iterable
- Object with Symbol.iterator
- iterator
- Object with a next() method
- factory
- Fresh iterator each call
Next lesson: for...of in depth