Iterators (Symbol. iterator)
The iteration protocol: an object with a [Symbol.iterator]() method works with for...of, spread, and destructuring.
Example
#const tasks = ['Quiz', 'Project']
const iterator = tasks[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())This is exactly how a for...of loop works: it gets an iterator and calls next() until it gets done: true.
Definition and usage
#Iteration in JavaScript rests on a simple contract. An iterable object has a method keyed Symbol.iterator that returns an iterator. An iterator is an object with a next() method, which on every call returns a result { value, done }: the next value, and whether that was the last one.
for...of, spread [...x], array destructuring, Array.from(), the Map and Set constructors, and Promise.all() all rely on this contract. Arrays, strings, Map, Set, and NodeLists are iterable out of the box. A plain object {} is not, which is why for...of on one throws a TypeError.
You make your own object or class iterable by adding a [Symbol.iterator]() method. The simplest way to write it is as a generator, *[Symbol.iterator]() {}, since generators build correct iterators on their own. Browsers shipped since 2025 also have iterator helper methods, like map(), filter(), take(), and toArray(), which process values lazily, without building intermediate arrays.
Syntax
#const iterator = iterable[Symbol.iterator]()
iterator.next()
class Name {
*[Symbol.iterator]() {
yield value
}
}More examples
#class Course {
constructor(title, lessons) {
this.title = title
this.lessons = lessons
}
*[Symbol.iterator]() {
for (const lesson of this.lessons) {
yield `${this.title}: ${lesson}`
}
}
}
const course = new Course('CSS', ['Selectors', 'Flexbox', 'Grid'])
for (const lesson of course) {
console.log(lesson)
}
console.log([...course].length)const countdown = {
from: 3,
[Symbol.iterator]() {
let current = this.from
return {
next() {
if (current > 0) {
return { value: current--, done: false }
}
return { value: undefined, done: true }
}
}
}
}
console.log([...countdown])
const [first, second] = countdown
console.log(first, second)The same object works with spread and with destructuring, because both constructs call [Symbol.iterator]() and pull values through next().
const scores = { Emma: 120, Jack: 95 }
try {
for (const score of scores) {
console.log(score)
}
} catch (error) {
console.log(error.name)
}
console.log(typeof scores[Symbol.iterator])
console.log(typeof [][Symbol.iterator])
for (const [name, points] of Object.entries(scores)) {
console.log(name, points)
}Browser support
#Widely available
Works in all modern browsers, phones included. You can use it without worry.
- Chrome
- Edge
- Firefox
- Safari
Good practices
#- The easiest way to write
[Symbol.iterator]()is as a generator withyield. You don’t have to build{ value, done }objects by hand that way. - An iterable object, like an array, creates a new iterator on every loop, so you can walk it multiple times. The iterator or generator itself returns no further values once exhausted.
- To check whether a value can be walked with
for...of, use the conditiontypeof value?.[Symbol.iterator] === "function".
Related entries
#- function*Declares a generator: a function that hands back successive values through yield and pauses between them.
- for...ofWalks the successive values of an iterable object: an array, a string, a Map, a Set, and others.
- Spread (...)The spread syntax spreads an array, string, or object out into individual elements: for copying, merging, and passing arguments.
- SymbolA primitive type for creating unique identifiers, such as property keys that never collide with others.
- Array.from()Creates an array from an iterable or "array-like" object.
- DestructuringDestructuring pulls values out of arrays and objects straight into separate variables.
Spotted a mistake or missing an example? Write to us.