# JavaScript Iterators: How They Work and How to Use Them

Source: https://devdocs10.com/articles/javascript/js-iterators  
By Murat Bo · Updated 2026-10-17 · DevDocs10 (https://devdocs10.com)

JavaScript iterators are the low-level objects that let you step through a sequence of values one item at a time. They power features like `for...of`, the spread operator, array destructuring, and many built-in data structures.

> **Quick answer:** An iterator is an object with a `next()` method that returns `{ value, done }`. You usually do not build one manually unless you need custom iteration behavior; most of the time you use iterable objects like arrays, strings, maps, sets, or generator functions.

> **Difficulty:** Beginner
>
> **You'll understand this better if you know:** basic JavaScript objects, arrays, and how `for...of` loops read values from a collection.

## 1. What Is a JavaScript Iterator?

A JavaScript iterator is an object that returns the next value in a sequence each time you ask for it. It follows a simple protocol: call `next()`, get back an object with a `value` and a `done` property, and repeat until `done` becomes `true`.

- `next()` advances through the sequence.
- `value` is the current item.
- `done` tells you whether the iterator is finished.
- Iterators are usually created by iterable objects such as arrays, strings, maps, sets, and custom objects.

An important distinction is that an `iterator` is not the same thing as an `iterable`. An iterable is a value that can produce an iterator, usually through `Symbol.iterator`.

## 2. Why JavaScript Iterators Matter

Iterators matter because they give JavaScript a common way to read sequences, whether the data comes from an array, a map, a set, a string, or your own custom type. They make loops and other language features work consistently across many kinds of data.

They are especially useful when you want to:

- process values one at a time instead of all at once,
- build custom collection types that work with `for...of`,
- create lazy or on-demand sequences,
- read data from APIs or generators in a controlled way.

For most beginners, the biggest benefit is practical: you already use iterators indirectly whenever you loop over modern JavaScript collections.

## 3. Basic Syntax

The simplest iterator has a `next()` method. Each call returns an `IteratorResult` object with the shape `{ value: ..., done: ... }`.

### Minimal iterator example

This example shows the core idea without involving any loop syntax yet.

```javascript
const iterator = {
  next() {
    return { value: 1, done: false };
  }
};

const result = iterator.next();

console.log(result);
// { value: 1, done: false }
```

Output (tested with Node 24.21.0):

```text
{ value: 1, done: false }
```

This iterator always returns the same result, so it is not very useful yet. Real iterators usually change internal state each time `next()` is called.

### Iterator with internal state

Here the iterator moves through a small sequence and stops when it reaches the end.

```javascript
const iterator = {
  current: 0,
  next() {
    if (this.current < 3) {
      return { value: this.current++, done: false };
    }

    return { value: undefined, done: true };
  }
};
```

Each call advances `current` until the sequence is exhausted. After that, the iterator signals completion with `done: true`.

## 4. Step-by-Step Examples

### Example 1: Reading values manually

You can call `next()` yourself to see exactly how iteration works.

```javascript
const letters = ["a", "b", "c"];
const iterator = letters[Symbol.iterator]();

console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
```

Output (tested with Node 24.21.0):

```text
{ value: "a", done: false }
{ value: "b", done: false }
{ value: "c", done: false }
{ value: undefined, done: true }
```

The array provides an iterator through `Symbol.iterator`. Each call returns the next array item until the sequence ends.

### Example 2: Using an iterator with a `for...of` loop

Most of the time, you do not call `next()` yourself. JavaScript does it for you inside `for...of`.

```javascript
const numbers = [10, 20, 30];

for (const number of numbers) {
  console.log(number);
}
```

Output (tested with Node 24.21.0):

```text
10
20
30
```

This works because arrays are iterable, and iterable objects can produce an iterator behind the scenes.

### Example 3: Checking the iterator result shape

Iterators always return a result object with the same basic structure, even when they are done.

```javascript
const iterator = ["x"][Symbol.iterator]();

const first = iterator.next();
const second = iterator.next();

console.log(first);
console.log(second);
```

Output (tested with Node 24.21.0):

```text
{ value: "x", done: false }
{ value: undefined, done: true }
```

The first call returns the value `"x"`. The second call indicates that iteration is finished.

### Example 4: A custom iterator for a range

This is a realistic pattern when you want an object to behave like a sequence.

```javascript
const range = {
  start: 1,
  end: 3,
  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;

    return {
      next() {
        if (current <= end) {
          return { value: current++, done: false };
        }

        return { value: undefined, done: true };
      }
    };
  }
};

for (const value of range) {
  console.log(value);
}
```

Output (tested with Node 24.21.0):

```text
1
2
3
```

This object becomes iterable because it defines `Symbol.iterator` and returns an iterator object.

## 5. Practical Use Cases

- Looping through arrays, strings, maps, and sets with `for...of`.
- Creating custom collections such as ranges, queues, or tree traversal helpers.
- Making object-like data usable with spread syntax, such as `[...myCollection]`.
- Building lazy data pipelines where values are generated only when needed.
- Writing reusable utilities that work with any iterable input, not just arrays.

Iterators are also useful when you want to separate the idea of *how to produce values* from *how to consume them*. That separation makes code more flexible and easier to reuse.

## 6. Common Mistakes

### Mistake 1: Confusing an iterator with an iterable

Beginners often assume any object that “can be looped over” is itself an iterator. In JavaScript, the iterable produces the iterator; the iterator actually returns values.

**Problem:** This code tries to use `next()` on an array directly, but arrays do not have a `next()` method.

```javascript
const items = ["a", "b"];

console.log(items.next());
```

Output (tested with Node 24.21.0):

```text
Uncaught TypeError: items.next is not a function
```

**Fix:** Ask the array for its iterator first by calling `Symbol.iterator`.

```javascript
const items = ["a", "b"];
const iterator = items[Symbol.iterator]();

console.log(iterator.next());
```

Output (tested with Node 24.21.0):

```text
{ value: "a", done: false }
```

The fixed version works because `Symbol.iterator` returns the iterator object for the array.

### Mistake 2: Returning the wrong shape from `next()`

Iterator results must have the expected `{ value, done }` shape. If `done` is missing or spelled incorrectly, consuming code may behave unexpectedly.

**Problem:** This custom iterator returns a plain value instead of an iterator result object, so consumers like `for...of` cannot use it correctly.

```javascript
const badIterable = {
  [Symbol.iterator]() {
    return {
      next() {
        return 1;
      }
    };
  }
};

for (const value of badIterable) {
  console.log(value);
}
```

Output (tested with Node 24.21.0):

```text
Uncaught TypeError: Iterator result 1 is not an object
```

**Fix:** Return an object with both `value` and `done`.

```javascript
const goodIterable = {
  [Symbol.iterator]() {
    let count = 0;

    return {
      next() {
        if (count < 1) {
          return { value: count++, done: false };
        }

        return { value: undefined, done: true };
      }
    };
  }
};
```

The corrected iterator works because consuming code can recognize when the sequence ends.

### Mistake 3: Reusing an exhausted iterator

Iterators are stateful and usually one-time-use. Once an iterator is done, calling `next()` again keeps returning `done: true`.

**Problem:** This code expects the same iterator to start over, but iterators do not reset automatically.

```javascript
const iterator = [1, 2][Symbol.iterator]();

console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
// Still exhausted
```

Output (tested with Node 24.21.0):

```text
{ value: 1, done: false }
{ value: 2, done: false }
{ value: undefined, done: true }
```

**Fix:** Create a fresh iterator when you need to iterate again, or use the iterable again instead of storing the iterator.

```javascript
const items = [1, 2];

for (const value of items) {
  console.log(value);
}

for (const value of items) {
  console.log(value);
}
```

Output (tested with Node 24.21.0):

```text
1
2
1
2
```

The fixed version works because each loop asks the array for a new iterator internally.

## 7. Best Practices

### Practice 1: Use iterables, not raw iterators, in most code

If your goal is simply to loop over data, prefer an iterable like an array, set, or custom iterable object. That gives you reusable iteration instead of a one-time cursor.

```javascript
const numbers = [1, 2, 3];

for (const number of numbers) {
  console.log(number);
}
```

Output (tested with Node 24.21.0):

```text
1
2
3
```

This is better than storing a single iterator because the iterable can be consumed multiple times.

### Practice 2: Keep iterator state private

Custom iterators are easier to reason about when their moving parts are hidden inside the iterator factory, not exposed as public fields.

```javascript
const counter = {
  [Symbol.iterator]() {
    let value = 0;

    return {
      next() {
        if (value >= 3) {
          return { value: undefined, done: true };
        }

        return { value: value++, done: false };
      }
    };
  }
};
```

Private state helps prevent accidental mutation from the outside and keeps the iteration behavior predictable.

### Practice 3: Return a fresh iterator each time

If an object is iterable, its `Symbol.iterator` method should usually create a new iterator for each call.

```javascript
const bag = {
  items: ["pen", "notebook"],
  [Symbol.iterator]() {
    let index = 0;
    const items = this.items;

    return {
      next() {
        if (index < items.length) {
          return { value: items[index++], done: false };
        }

        return { value: undefined, done: true };
      }
    };
  }
};
```

Each iteration gets its own cursor, so the object can be looped over more than once.

## 8. Limitations and Edge Cases

- Iterators are usually one-way and stateful. Once consumed, they do not reset unless you create a new one.
- An iterator alone is not enough for `for...of`. The object also needs to be iterable through `Symbol.iterator`.
- Not every loop in JavaScript uses iterators. Traditional `for` and `while` loops do not require the iterator protocol.
- Some array methods such as `map`, `filter`, and `reduce` do not consume iterators directly; they operate on arrays.
- If a custom iterator throws an error during `next()`, iteration stops immediately and the error propagates to the caller.
- In browser and Node.js environments, strings, arrays, maps, and sets are iterable, but plain objects are not iterable by default.

A common error message is `TypeError: object is not iterable`. This usually means you passed a non-iterable value to something that expects an iterable, such as spread syntax, `for...of`, or array destructuring.

## 9. Practical Mini Project

Let’s build a small custom iterable that yields the first few even numbers. This example brings together the iterator protocol, `Symbol.iterator`, and `for...of`.

```javascript
const evenNumbers = {
  limit: 5,
  [Symbol.iterator]() {
    let count = 0;
    let current = 0;
    const limit = this.limit;

    return {
      next() {
        if (count >= limit) {
          return { value: undefined, done: true };
        }

        const value = current;
        current += 2;
        count++;

        return { value: value, done: false };
      }
    };
  }
};

for (const number of evenNumbers) {
  console.log(number);
}
```

Output (tested with Node 24.21.0):

```text
0
2
4
6
8
```

This iterable yields `0, 2, 4, 6, 8`. It is small, self-contained, and works naturally with any code that expects an iterable.

## 10. Key Points

- An iterator is an object with a `next()` method.
- `next()` returns `{ value, done }`.
- An iterable is a value that can create an iterator, usually through `Symbol.iterator`.
- `for...of`, spread syntax, and destructuring use iterators behind the scenes.
- Custom iterators are useful when built-in collections do not match your data shape.
- Iterators are stateful and usually one-time-use.

## 11. Practice Exercise

Build a custom iterable that generates the first four squares: `1, 4, 9, 16`.

- Create an object with a `Symbol.iterator` method.
- Make the iterator return one square at a time.
- Use `for...of` to print the values.

**Expected output:**

```javascript
1
4
9
16
```

**Hint:** Keep a counter inside the iterator and multiply it by itself before returning each value.

**Solution:**

```javascript
const squares = {
  [Symbol.iterator]() {
    let n = 1;

    return {
      next() {
        if (n > 4) {
          return { value: undefined, done: true };
        }

        const value = n * n;
        n++;

        return { value: value, done: false };
      }
    };
  }
};

for (const value of squares) {
  console.log(value);
}
```

Output (tested with Node 24.21.0):

```text
1
4
9
16
```

## 12. Final Summary

JavaScript iterators are the mechanism that lets you step through values one at a time. They are simple at the protocol level: call `next()`, read `value`, and stop when `done` becomes `true`. What makes them powerful is that they sit underneath many everyday language features, including `for...of`, spread syntax, and destructuring.

In practice, you will usually work with iterables rather than raw iterators. Arrays, strings, maps, and sets already support iteration, and custom iterable objects let you define your own sequences when needed. Once you understand the difference between an iterable and an iterator, the rest of JavaScript’s iteration model becomes much easier to use correctly.

Next, try writing a custom iterable that yields file names, quiz questions, or numbers from a business rule. The more you practice with `Symbol.iterator` and `next()`, the more natural iterator-based code will feel.

## Browser Support

| Feature | Status | Chrome | Edge | Firefox | Safari | Safari iOS |
| --- | --- | --- | --- | --- | --- | --- |
| Iterator methods | Newly available since Mar 2025 | 122+ | 122+ | 131+ | 18.4+ | 18.4+ |
| Array iterators | Widely available since Nov 2020 | 66+ | 14+ | 60+ | 10+ | 10+ |

Data: web-features 3.43.0 (MDN Baseline).
