JavaScript Iterables and Iterators
In the vast and dynamic landscape of JavaScript programming, understanding iterables is pivotal. Iterables are a foundational concept, essential for efficiently
Introduction to JavaScript Iterables
JavaScript iterables are objects that implement a specific protocol, allowing them to be consumed by iteration constructs like for...of. This guide covers the core concepts, built-in iterables, and practical techniques for working with collections.
What are Iterables in JavaScript?
At its core, an iterable is an object that implements the Symbol.iterator method, enabling sequential access to its elements. Several built-in types in JavaScript are iterable, including Array, String, Map, Set, and more. It is important to distinguish between an iterable (the object being traversed) and an iterator (the object returned by Symbol.iterator that actually performs the traversal). These iterables are integral to various operations, such as looping and data manipulation.
For a detailed exploration of JavaScript Map and Set objects, please refer to our comprehensive guide on JavaScript Map and Set.
Example of an Iterable: Array
Let's look at a basic example of an iterable in JavaScript:
This code snippet demonstrates iterating over an array of fruits, a common iterable. The for...of loop automatically calls the iterable's Symbol.iterator method and consumes the resulting iterator until done is true. See JavaScript loops for the full family of looping constructs.
Do not confuse for...of with for...in. for...of iterates over the values produced by an iterable (array items, string characters, Map entries). for...in iterates over the enumerable property keys of an object — including inherited ones — and is meant for plain objects, not arrays. Using for...in on an array gives you index strings ("0", "1", …) and can pick up extra properties, so prefer for...of for ordered data.
The Iterator Protocol
The cornerstone of an iterable is its Symbol.iterator method. The protocol is a precise contract:
- An iterable has a method keyed by
Symbol.iterator. Calling it returns an iterator. - An iterator is an object with a
next()method. - Each call to
next()returns an object{ value, done }:done: false—valueis the next item in the sequence.done: true— iteration is finished (valueis then ignored, or carries an optional final result).
Any object that follows this contract works with for...of, the spread operator, destructuring, and Array.from() — even if you wrote the object yourself. Symbol is a built-in unique key; see Symbol type for why these protocol methods use one instead of a plain string name.
Example: A custom range iterable
A classic use case is an object that produces a numeric range lazily, without ever building an array:
In this example, the [Symbol.iterator]() method returns a fresh iterator each time, so the same range object can be looped over more than once. The method shorthand syntax ensures this correctly refers to the range object. (Using an arrow function for [Symbol.iterator] would capture this lexically and break the pattern.)
Generators: the easy way to build iterables
Writing next() and tracking state by hand is verbose. A generator function — declared with function* and using yield — produces an iterator automatically. Each yield pauses the function and hands a value to the consumer; execution resumes on the next next() call. The same range becomes far shorter:
The * before the method name makes it a generator method, so range is iterable with almost no boilerplate. For everything generators can do — including two-way communication and delegation with yield* — see Generators and Async iterators and generators.
Infinite and lazy sequences
Because an iterator only computes the next value when asked, it can describe sequences that are too large — or even infinite — to hold in memory. This is the main reason to write a custom iterator instead of just using an array:
Never use spread ([...naturals()]) or for...of without a break on an infinite iterable — it will loop forever. Pull a finite number of values with next() instead.
Consuming Iterables
Once an object is iterable, the whole language opens up to it: any construct that accepts an iterable works with your custom type the same way it works with arrays.
Using Array.from()
The Array.from() method creates a new array from any iterable (or array-like) object. It also accepts an optional map function as a second argument, applied to every element as the array is built — handier than Array.from(it).map(fn) because it avoids a second pass:
See JavaScript Map and Set for more on Set, and Array methods for what you can do once you have an array.
Spread Syntax with Iterables
Spread syntax (...) expands an iterable wherever arguments or elements are expected — merging arrays, copying, or passing items as function arguments:
For the full picture of ... in both spread and collect positions, see Rest parameters and spread syntax.
Destructuring and rest
Destructuring assignment pulls values out of any iterable by position, and the rest pattern (...) gathers what's left into an array:
Learn more in Destructuring assignment.
The String iterable and Unicode safety
Strings are iterable, and crucially the iterator walks Unicode code points, not 16-bit code units. That means surrogate-pair characters (emoji, some scripts) are kept intact — unlike indexing with [i] or older for loops over .length, which can split them:
Whenever you need to count or split user-facing characters correctly, iterate the string (or spread it) rather than relying on .length. See the Strings chapter for more.
Summary
- An object is iterable when it has a
[Symbol.iterator]()method that returns an iterator — an object whosenext()yields{ value, done }. - Reach for a generator (
function*/yield) instead of writingnext()by hand; it's the shortest, least error-prone way to make something iterable. - Use
for...offor the values of ordered/iterable data; usefor...inonly for the keys of plain objects. - Once iterable, your type plugs into spread, destructuring, rest,
Array.from(it, mapFn), and many built-in APIs for free. - Iterators are lazy, so they can model infinite or huge sequences that an array never could.
- Iterate strings (don't index them) to handle Unicode characters like emoji safely.