Skip to content

Iterators and Iterables

Iterators define how to iterate over custom objects. Iterables are objects that implement the @@iterator method (Symbol.iterator). Introduced in ES6 (2015).

Could only iterate built-in types with for loops:

// Arrays only
const arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// Custom objects not iterable
const obj = { a: 1, b: 2, c: 3 };
// for (const val of obj) {} // TypeError

Any object can be made iterable:

// Built-in iterables
const str = "hello";
for (const char of str) {
console.log(char); // h, e, l, l, o
}
const arr = [1, 2, 3];
for (const num of arr) {
console.log(num);
}
// Custom iterable
const range = {
from: 1,
to: 5,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
if (current <= last) {
return { value: current++, done: false };
} else {
return { done: true };
}
}
};
}
};
for (const num of range) {
console.log(num); // 1, 2, 3, 4, 5
}
// Spread with iterables
const arr = [...range]; // [1, 2, 3, 4, 5]
// Destructuring with iterables
const [first, second, ...rest] = range;
// Iterator protocol manually
const iterator = range[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
// String iterator
const str = "A\uD83D\uDE80B";
const iterator = str[Symbol.iterator]();
console.log(iterator.next().value); // "A"
console.log(iterator.next().value); // "🚀"
console.log(iterator.next().value); // "B"
// Custom collection
class Collection {
constructor(...items) {
this.items = items;
}
[Symbol.iterator]() {
let index = 0;
const items = this.items;
return {
next() {
if (index < items.length) {
return { value: items[index++], done: false };
}
return { done: true };
}
};
}
}
const coll = new Collection(1, 2, 3);
for (const item of coll) {
console.log(item);
}
// Infinite iterator
const naturals = {
[Symbol.iterator]() {
let n = 1;
return {
next() {
return { value: n++, done: false };
}
};
}
};
// Take first 5
const first5 = [];
for (const num of naturals) {
first5.push(num);
if (first5.length >= 5) break;
}
  • Customizable: Define iteration for any object
  • Protocol: Standard interface for iteration
  • Lazy: Values produced on demand
  • Composable: Works with for…of, spread, destructuring
  • Flexible: Can be finite or infinite
  • Iterator protocol: next() returns { value, done }
  • Iterable protocol: has [Symbol.iterator]() method
  • Generators are iterators and iterables
  • Built-in iterables: String, Array, Map, Set, arguments
  • Can implement both protocols in same object
// Object that's both iterable and iterator
const counter = {
count: 0,
max: 3,
[Symbol.iterator]() {
return this;
},
next() {
if (this.count < this.max) {
return { value: this.count++, done: false };
}
return { done: true };
}
};
// Works, but only once!
for (const n of counter) {
console.log(n); // 0, 1, 2
}
for (const n of counter) {
// Nothing - already exhausted
}
// Better: return fresh iterator each time
const betterCounter = {
max: 3,
[Symbol.iterator]() {
let count = 0;
return {
next: () => {
if (count < this.max) {
return { value: count++, done: false };
}
return { done: true };
}
};
}
};
  1. Make this object iterable: { values: [1, 2, 3] }

    Answer
    const obj = {
    values: [1, 2, 3],
    [Symbol.iterator]() {
    let index = 0;
    return {
    next: () => ({
    value: this.values[index++],
    done: index > this.values.length
    })
    };
    }
    };
  2. What does for...of require from an object?

    Answer The object must be iterable, meaning it has a `[Symbol.iterator]()` method that returns an iterator.
  3. Create an iterable that yields 1, 2, 3

    Answer
    const iterable = {
    [Symbol.iterator]: function* () {
    yield 1;
    yield 2;
    yield 3;
    }
    };