Iterators and Iterables
Iterators and Iterables
Section titled “Iterators and Iterables”What it is
Section titled “What it is”Iterators define how to iterate over custom objects. Iterables are objects that implement the @@iterator method (Symbol.iterator). Introduced in ES6 (2015).
Before this feature
Section titled “Before this feature”Could only iterate built-in types with for loops:
// Arrays onlyconst arr = [1, 2, 3];for (let i = 0; i < arr.length; i++) { console.log(arr[i]);}
// Custom objects not iterableconst obj = { a: 1, b: 2, c: 3 };// for (const val of obj) {} // TypeErrorAfter this feature
Section titled “After this feature”Any object can be made iterable:
// Built-in iterablesconst 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 iterableconst 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 iterablesconst arr = [...range]; // [1, 2, 3, 4, 5]
// Destructuring with iterablesconst [first, second, ...rest] = range;
// Iterator protocol manuallyconst iterator = range[Symbol.iterator]();console.log(iterator.next()); // { value: 1, done: false }console.log(iterator.next()); // { value: 2, done: false }
// String iteratorconst 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 collectionclass 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 iteratorconst naturals = { [Symbol.iterator]() { let n = 1; return { next() { return { value: n++, done: false }; } }; }};
// Take first 5const first5 = [];for (const num of naturals) { first5.push(num); if (first5.length >= 5) break;}Why this is better
Section titled “Why this is better”- 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
Key notes / edge cases
Section titled “Key notes / edge cases”- 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 iteratorconst 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 timeconst betterCounter = { max: 3,
[Symbol.iterator]() { let count = 0; return { next: () => { if (count < this.max) { return { value: count++, done: false }; } return { done: true }; } }; }};Quick practice
Section titled “Quick practice”-
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})};}}; -
What does
for...ofrequire from an object?Answer
The object must be iterable, meaning it has a `[Symbol.iterator]()` method that returns an iterator. -
Create an iterable that yields 1, 2, 3
Answer
const iterable = {[Symbol.iterator]: function* () {yield 1;yield 2;yield 3;}};