Iterators (Symbol.iterator)

The iteration protocol: an object with a [Symbol.iterator]() method works with for...of, spread, and destructuring.

On this page

Example

#
JavaScript
const tasks = ['Quiz', 'Project']
const iterator = tasks[Symbol.iterator]()

console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
Console
{ value: 'Quiz', done: false }
{ value: 'Project', done: false }
{ value: undefined, done: true }

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

#
Syntax
const iterator = iterable[Symbol.iterator]()
iterator.next()

class Name {
  *[Symbol.iterator]() {
    yield value
  }
}

More examples

#
A custom iterable class
JavaScript
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)
Console
CSS: Selectors
CSS: Flexbox
CSS: Grid
3
An iterator written by hand
JavaScript
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)
Console
[3, 2, 1]
3 2

The same object works with spread and with destructuring, because both constructs call [Symbol.iterator]() and pull values through next().

Pitfall: a plain object is not iterable
JavaScript
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)
}
Console
TypeError
undefined
function
Emma 120
Jack 95

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 with yield. 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 condition typeof value?.[Symbol.iterator] === "function".

Related entries

#

Spotted a mistake or missing an example? Write to us.