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→trueif 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...offor 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
