JavaScript Iterator Interface and next() Method
In JavaScript, the Iterator interface and the next()
method provide a standardized mechanism for traversing sequences of data
one element at a time. This article explores how the iteration protocol
functions under the hood, the structure and role of the
next() method, and how JavaScript utilizes these constructs
to power modern language features like the for...of loop,
destructuring, and the spread operator.
The Iteration Protocol
The JavaScript iteration protocol defines how any object can become iterable. It is split into two distinct protocols:
- The Iterable Protocol: An object must define a
method at the key
[Symbol.iterator]. This method is a factory that returns an Iterator object. - The Iterator Protocol: An object must implement a
standard interface containing a
next()method.
The Role of the next()
Method
The next() method is the core execution mechanism of an
iterator. It acts as a cursor that moves through a data structure,
maintaining internal state across invocations.
Every time next() is called, it must return an object
with two specific properties:
value: The current value in the iteration sequence. This can be any valid JavaScript value orundefinedwhen the sequence is complete.done: A boolean flag. It is set tofalseif more values are available, andtrueonce the iterator has reached the end of the sequence.
Return Object Structure
{
value: any,
done: boolean
}When done becomes true, subsequent calls to
next() should continue returning
{ value: undefined, done: true }.
Implementing a Custom Iterator
To understand how the interface functions, consider a custom range iterator that generates numbers from a starting point to an endpoint:
function createRangeIterator(start = 0, end = Infinity, step = 1) {
let nextIndex = start;
let count = 0;
return {
next() {
if (nextIndex <= end) {
const result = { value: nextIndex, done: false };
nextIndex += step;
count++;
return result;
}
return { value: count, done: true };
}
};
}
const range = createRangeIterator(1, 3);
console.log(range.next()); // { value: 1, done: false }
console.log(range.next()); // { value: 2, done: false }
console.log(range.next()); // { value: 3, done: false }
console.log(range.next()); // { value: 3, done: true }Making an Object Iterable
To allow an object to be consumed by native syntax like
for...of, the iterator must be attached to the
Symbol.iterator property:
const collection = {
items: ['alpha', 'beta', 'gamma'],
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.items.length) {
return { value: this.items[index++], done: false };
}
return { value: undefined, done: true };
}
};
}
};
for (const item of collection) {
console.log(item); // Logs: 'alpha', 'beta', 'gamma'
}Native Language Integration
The Iterator interface and next() method power multiple
native JavaScript features:
for...ofLoops: Automatically requests the iterator from[Symbol.iterator]and calls.next()untildone: true.- Spread Operator (
...): Extracts elements by exhausting an iterator into arrays or function arguments. - Array Destructuring: Selects values sequentially
using
next(). - Built-in Iterables: Built-in types such as
Array,String,Map,Set, andTypedArrayimplement this exact interface by default.