JavaScript Iterators

An iterator is an object that defines a next() method which returns objects with two properties: value (the next value) and done (a boolean indicating if the sequence is complete). Iterators power the for...of loop and other iteration constructs.

The Iterator Protocol

Any object that has a next() method returning { value, done } objects follows the iterator protocol. When done is true, the iteration is complete.

Iterator Protocol
// Get iterator from an array
const arr = ["a", "b", "c"];
const iterator = arr[Symbol.iterator]();

// Call next() manually
console.log(iterator.next()); // { value: 'a', done: false }
console.log(iterator.next()); // { value: 'b', done: false }
console.log(iterator.next()); // { value: 'c', done: false }
console.log(iterator.next()); // { value: undefined, done: true }

// String iterator
const strIter = "Hi"[Symbol.iterator]();
console.log(strIter.next());
console.log(strIter.next());
console.log(strIter.next());

Creating Custom Iterators

You can create your own iterators by defining an object with a next() method. To make an object iterable, add a [Symbol.iterator]() method that returns an iterator.

Custom Iterator
// Simple range iterator
function createRange(start, end) {
  let current = start;
  return {
    next() {
      if (current <= end) {
        return { value: current++, done: false };
      }
      return { value: undefined, done: true };
    }
  };
}

const range = createRange(1, 5);
console.log(range.next());
console.log(range.next());
console.log(range.next());
console.log(range.next());
console.log(range.next());
console.log(range.next());

Making Objects Iterable

To use for...of and spread on your own objects, implement the [Symbol.iterator]() method that returns an iterator.

Iterable Object
// Make a range object iterable
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 { done: true };
      }
    };
  }
};

// Now works with for...of
for (const num of range) {
  console.log("Range:", num);
}

// And with spread
console.log("Spread:", [...range]);
📝 Note: The [Symbol.iterator]() method must return a new iterator each time it is called. This allows the object to be iterated multiple times independently.

Infinite Iterators

Iterators don't have to end. You can create infinite sequences -- just be careful to use break or a limiting mechanism when consuming them.

Infinite Iterator
// Infinite counter
const counter = {
  [Symbol.iterator]() {
    let n = 0;
    return {
      next() {
        return { value: n++, done: false };
      }
    };
  }
};

// Take first 5 values
const first5 = [];
for (const n of counter) {
  if (n >= 5) break;
  first5.push(n);
}
console.log("First 5:", first5);

// Fibonacci iterator
function fibonacci() {
  let a = 0, b = 1;
  return {
    [Symbol.iterator]() { return this; },
    next() {
      const value = a;
      [a, b] = [b, a + b];
      return { value, done: false };
    }
  };
}

const fib = fibonacci();
const first10 = [];
for (const n of fib) {
  if (n > 50) break;
  first10.push(n);
}
console.log("Fibonacci:", first10);

Iterator Helpers

Understanding the iterator pattern helps you work with built-in iterables more effectively and create reusable utility functions.

Iterator Utilities
// Take: get first n items from any iterable
function take(iterable, count) {
  const result = [];
  for (const item of iterable) {
    result.push(item);
    if (result.length >= count) break;
  }
  return result;
}

console.log("Take 3 from array:", take([10, 20, 30, 40, 50], 3));
console.log("Take 4 from string:", take("JavaScript", 4));

// Zip: combine two iterables
function* zip(a, b) {
  const iterA = a[Symbol.iterator]();
  const iterB = b[Symbol.iterator]();
  while (true) {
    const nextA = iterA.next();
    const nextB = iterB.next();
    if (nextA.done || nextB.done) break;
    yield [nextA.value, nextB.value];
  }
}

console.log("Zip:", [...zip([1, 2, 3], ["a", "b", "c"])]);
ConceptDescription
IteratorObject with next() returning {value, done}
IterableObject with [Symbol.iterator]() returning iterator
next()Returns {value: any, done: boolean}
done: falseMore values available
done: trueIteration complete
Symbol.iteratorWell-known symbol for the iterator method
Exercise:
What must the next() method of an iterator return?
Try it YourselfCtrl+Enter to run
Click Run to see the output here.