Iterables in JavaScript
In JavaScript, iterables are objects that can be looped over — meaning you can access their elements one by one using constructs like for...of
, the spread operator (...
), or destructuring.
An iterable is any object that implements the Symbol.iterator
method.
This method returns an iterator — an object that defines how to access values sequentially.
Common Built-in Iterables
JavaScript provides several built-in iterable types:
Iterable Type | Example |
---|---|
Array | [10, 20, 30] |
String | "Hello" |
Map | new Map([["a", 1], ["b", 2]]) |
Set | new Set([1, 2, 3]) |
Typed Arrays | new Uint8Array([5, 10, 15]) |
Arguments Object | Function’s arguments are iterable |
Generator Objects | Returned from generator functions |
How Iterables Work
An iterable must have a method with the key Symbol.iterator
, which returns an iterator object.
An iterator must implement a next()
method that returns an object with two properties:
value
→ The next value in the sequencedone
→true
if iteration is complete, otherwisefalse
Example:
const myArray = [10, 20, 30];
const iterator = myArray[Symbol.iterator]();
console.log(iterator.next()); // { value: 10, done: false }
console.log(iterator.next()); // { value: 20, done: false }
console.log(iterator.next()); // { value: 30, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
Using Iterables
Iterables provide a consistent way to loop over various data structures like arrays, strings, sets, and maps. By implementing the Iterable Protocol using Symbol.iterator
, any object can define how it should be iterated, giving full control over its looping behavior.
a. for...of Loop
Iterates over iterable values directly:
const fruits = ["apple", "banana", "cherry"];
for (const fruit of fruits) {
console.log(fruit);
}
// Output: apple, banana, cherry
b. Spread Operator (...
)
Expands iterables into elements:
const arr = [1, 2, 3];
const newArr = [...arr, 4, 5];
console.log(newArr); // [1, 2, 3, 4, 5]
c. Destructuring
Extracts iterable values:
const [a, b, c] = "XYZ";
console.log(a, b, c); // X Y Z
d. Array.from()
Converts any iterable into an array:
const set = new Set([1, 2, 3]);
const arr = Array.from(set);
console.log(arr); // [1, 2, 3]
Custom Iterables
You can make your own iterable objects by defining the Symbol.iterator
method.
Example: Custom Iterable
const range = {
start: 1,
end: 5,
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
} else {
return { done: true };
}
}
};
}
};
for (const num of range) {
console.log(num);
}
// Output: 1 2 3 4 5
Iterables vs. Iterators
Concept | Description | Example |
---|---|---|
Iterable | Has a Symbol.iterator method | Array, Set, Map, String |
Iterator | Has a next() method that returns { value, done } | Result of calling array[Symbol.iterator]() |
So every iterable produces an iterator when Symbol.iterator
is called.
Checking if an Object is Iterable
You can check if an object is iterable by testing for Symbol.iterator
:
const obj = {};
console.log(typeof obj[Symbol.iterator]); // undefined (not iterable)
const arr = [];
console.log(typeof arr[Symbol.iterator]); // function (iterable)
When to Use Iterables
- Looping over sequences (arrays, maps, sets, strings)
- Using
for...of
for cleaner iteration - Creating custom data structures (like ranges or linked lists)
- Integrating with Generators for lazy evaluation
Summary Table
Feature | Description | Example |
---|---|---|
Symbol.iterator | Defines an object as iterable | [Symbol.iterator]() { ... } |
for...of | Loops through iterable values | for (x of arr) |
next() | Returns { value, done } from iterator | iterator.next() |
Spread (... ) | Expands iterable | [...iterable] |
Destructuring | Unpacks values from iterable | [a, b] = iterable |
Array.from() | Converts iterable → array | Array.from(set) |
Custom Iterable with Class
Example using classes and private fields:
class Car {
#year;
constructor(name, year) {
this.name = name;
this.#year = year;
}
getYear() {
return this.#year;
}
}
class CarRegistry {
#cars = [new Car("Ford", 2014), new Car("Audi", 2019)];
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.#cars.length) {
return { value: this.#cars[index++], done: false };
}
return { done: true };
}
};
}
}
const registry = new CarRegistry();
for (const car of registry) {
console.log(car.name); // Ford, Audi
}
console.log([...registry].map(car => car.name)); // ["Ford", "Audi"]
Real-world Example
Iterating over a Map:
const userRoles = new Map([
["Alice", "Admin"],
["Bob", "Editor"],
["Charlie", "Viewer"]
]);
for (const [user, role] of userRoles) {
console.log(`${user}: ${role}`);
}
// Output:
// Alice: Admin
// Bob: Editor
// Charlie: Viewer