Skip to main content

JavaScript Generators & Iterators

JavaScript Custom Iterables

Written by Published

Adding a Symbol.iterator method makes your own object work with for...of.

Adding a Symbol.iterator method makes your own object work with for of.

This is how you plug a custom type into every language feature that expects an iterable.

A generator is the easiest way to implement it.

Example

Example

javascript

const range = {
  from: 1,
  to: 3,
  [Symbol.iterator]: function* () {
    for (let i = this.from; i <= this.to; i++) {
      yield i;
    }
  }
};

console.log([...range].join(","));

The output is 1,2,3.

Making an Object Iterable

Add a method named [Symbol.iterator].

Making it a generator function is the simplest approach.

Once added, for of, spread and destructuring all work on it.

Syntax

Syntax

javascript

const obj = {
  [Symbol.iterator]: function* () {
    yield value;
  }
};

The square brackets are required; Symbol.iterator is a computed key.

Using for of Directly

No manual iterator handling is needed once this is in place.

Example

Example

javascript

const range = {
  from: 1,
  to: 4,
  [Symbol.iterator]: function* () {
    for (let i = this.from; i <= this.to; i++) {
      yield i;
    }
  }
};

const values = [];
for (const n of range) {
  values.push(n);
}

console.log(values.join(","));

The output is 1,2,3,4.

A Class with Its Own Iteration

The same method works inside a class.

Example

Example

javascript

class Range {
  constructor(from, to) {
    this.from = from;
    this.to = to;
  }
  *[Symbol.iterator]() {
    for (let i = this.from; i <= this.to; i++) {
      yield i;
    }
  }
}

const range = new Range(1, 3);

console.log([...range].join(","));

Every instance of the class is now iterable.

Iterating Over Something More Interesting

A generator can yield anything, not just numbers.

Example

Example

javascript

class Playlist {
  constructor(songs) {
    this.songs = songs;
  }
  *[Symbol.iterator]() {
    for (const song of this.songs) {
      yield song.toUpperCase();
    }
  }
}

const playlist = new Playlist(["one", "two"]);

console.log([...playlist].join(","));

The output is ONE,TWO.

Without a Generator

It can also be written by hand, returning an iterator object directly.

Example

Example

javascript

const range = {
  from: 1,
  to: 3,
  [Symbol.iterator]() {
    let current = this.from;
    const to = this.to;
    return {
      next() {
        return current <= to
          ? { value: current++, done: false }
          : { value: undefined, done: true };
      }
    };
  }
};

console.log([...range].join(","));

Same result, more code - this is why the generator form is preferred.

Destructuring a Custom Iterable

Since it follows the protocol, destructuring works too.

Example

Example

javascript

const range = {
  from: 10,
  to: 20,
  [Symbol.iterator]: function* () {
    yield this.from;
    yield this.to;
  }
};

const [start, end] = range;

console.log(start + " to " + end);

The output is 10 to 20.

Complete Example

Complete Example

html

<!DOCTYPE html>
<html>
<head>
  <title>JavaScript Custom Iterables</title>
</head>
<body>

  <h1>Custom Iterables</h1>

  <p id="out"></p>

  <script>
    class Range {
      constructor(from, to, step = 1) {
        this.from = from;
        this.to = to;
        this.step = step;
      }
      *[Symbol.iterator]() {
        for (let i = this.from; i <= this.to; i += this.step) {
          yield i;
        }
      }
    }

    const evens = new Range(0, 10, 2);

    document.getElementById("out").textContent = [...evens].join(", ");
  </script>

</body>
</html>

Try It Yourself

Run the above example in the Try It Editor.

Try a different step:

Change the step to 3 and see the sequence change.

Important Points

  • A [Symbol.iterator] method makes an object iterable.
  • Writing it as a generator is the simplest approach.
  • Once added, for of, spread and destructuring all work.
  • Classes can define this the same way as plain objects.
  • An iterator can also be written by hand, without a generator.

Conclusion

This is how your own data types integrate with the rest of JavaScript.

A generator turns what would be fiddly code into a few natural lines.

Any class representing a collection is a strong candidate for this.