JavaScript 36 🧬 Iterator Interface
The Iterator interface defines a standard way to access elements of a collection one at a time. It’s the mechanism that powers for...of loops, spread syntax, and destructuring in JavaScript.
A Quick Look at the Example
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
Key idea: An iterator is an object with a next() method that returns { value, done } on each call.
What is an Iterator?
An iterator is an object that provides a standard way to traverse a sequence of values.
The Iterator Protocol
An object is an iterator if it implements a next() method that returns an object with two properties:
| Property | Type | Description |
|---|---|---|
value | any | The current value |
done | boolean | true when iteration is complete |
{
next() {
return { value: ..., done: ... };
}
}
The Iterable Protocol
An object is iterable if it has a Symbol.iterator method that returns an iterator.
{
[Symbol.iterator]() {
return {
next() {
return { value: ..., done: ... };
}
};
}
}
Relationship:
Iterable ──[Symbol.iterator]()──→ Iterator
│
└── next() → { value, done }
Built-in Iterables
Many JavaScript objects are iterable by default:
| Object | Iterable? | What You Get |
|---|---|---|
| Array | ✅ Yes | Values |
| String | ✅ Yes | Characters |
| Map | ✅ Yes | [key, value] pairs |
| Set | ✅ Yes | Values |
| NodeList | ✅ Yes | DOM nodes |
arguments | ✅ Yes | Arguments |
| Generator | ✅ Yes | Yielded values |
| TypedArray | ✅ Yes | Numeric values |
Plain Object {} | ❌ No | Not iterable |
Basic Example: Manual Iteration
const arr = [10, 20, 30];
const iterator = arr[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {
console.log(result.value);
result = iterator.next();
}
// 10
// 20
// 30
Step by step:
| Call | Returns | State |
|---|---|---|
iterator.next() | { value: 10, done: false } | Position 0 → 1 |
iterator.next() | { value: 20, done: false } | Position 1 → 2 |
iterator.next() | { value: 30, done: false } | Position 2 → 3 |
iterator.next() | { value: undefined, done: true } | Finished |
The for...of Loop
The for...of loop uses the iterator interface automatically.
const arr = [1, 2, 3];
for (const value of arr) {
console.log(value);
}
// 1
// 2
// 3
What happens under the hood:
// Equivalent to:
const iterator = arr[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {
const value = result.value;
console.log(value);
result = iterator.next();
}
for...of works with any iterable:
for (const char of "Hello") { } // Characters
for (const [k, v] of new Map()) { } // Entries
for (const v of new Set()) { } // Values
Creating Custom Iterables
You can make your own objects iterable by implementing Symbol.iterator.
Example 1: Range Object
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
}
return { value: undefined, done: true };
}
};
}
}
const range = new Range(1, 5);
for (const num of range) {
console.log(num);
}
// 1
// 2
// 3
// 4
// 5
// Works with spread!
console.log([...new Range(1, 5)]); // [1, 2, 3, 4, 5]
// Works with destructuring!
const [first, second] = new Range(10, 20);
console.log(first, second); // 10 20
Example 2: Countdown Object
class Countdown {
constructor(from) {
this.from = from;
}
[Symbol.iterator]() {
let current = this.from;
return {
next: () => {
if (current >= 0) {
return { value: current--, done: false };
}
return { value: undefined, done: true };
}
};
}
}
const countdown = new Countdown(3);
for (const num of countdown) {
console.log(num);
}
// 3
// 2
// 1
// 0
Note: The arrow function preserves
this— useful in iterators.
Example 3: Fibonacci Sequence
class Fibonacci {
constructor(limit) {
this.limit = limit;
}
[Symbol.iterator]() {
let a = 0, b = 1, count = 0;
const limit = this.limit;
return {
next() {
if (count >= limit) {
return { value: undefined, done: true };
}
const value = a;
[a, b] = [b, a + b];
count++;
return { value, done: false };
}
};
}
}
const fib = new Fibonacci(8);
console.log([...fib]); // [0, 1, 1, 2, 3, 5, 8, 13]
Generators — A Simpler Way
Generators are functions that return iterators automatically. They’re a much cleaner way to create iterables.
function* range(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
console.log([...range(1, 5)]); // [1, 2, 3, 4, 5]
for (const num of range(10, 13)) {
console.log(num);
}
// 10
// 11
// 12
// 13
Generator versions of the earlier examples:
// Range as generator
function* range(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
// Countdown as generator
function* countdown(from) {
for (let i = from; i >= 0; i--) {
yield i;
}
}
// Fibonacci as generator
function* fibonacci(limit) {
let a = 0, b = 1;
for (let i = 0; i < limit; i++) {
yield a;
[a, b] = [b, a + b];
}
}
Key insight: Generators implement
Symbol.iteratorautomatically — you don’t have to writenext()yourself.
Iterators Are Stateful (One-Time Use)
An iterator remembers its position and can only be consumed once.
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
// If you save the iterator and come back to it:
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
// Once done, it stays done:
console.log(iterator.next()); // { value: undefined, done: true }
Iterable vs Iterator:
| Concept | Description | Can Iterate Multiple Times? |
|---|---|---|
| Iterable | Has Symbol.iterator method | ✅ Yes (each call returns a new iterator) |
| Iterator | Has next() method | ❌ No (stateful, one-time use) |
const arr = [1, 2, 3]; // Iterable — can iterate many times
const iter = arr[Symbol.iterator](); // Iterator — consumed once
for (const x of arr) { } // ✅ First iteration — works
for (const x of arr) { } // ✅ Second iteration — still works
for (const x of iter) { } // ✅ First iteration — works
for (const x of iter) { } // ❌ Second iteration — empty! (exhausted)
Where Iterators Are Used
| Feature | Uses Iterator? |
|---|---|
for...of loop | ✅ Yes |
Spread [...arr] | ✅ Yes |
Destructuring [a, b] = arr | ✅ Yes |
Array.from(iterable) | ✅ Yes |
Promise.all(iterable) | ✅ Yes |
new Map(iterable) | ✅ Yes |
new Set(iterable) | ✅ Yes |
yield* in generators | ✅ Yes |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Iterator Interface</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
color: #333;
line-height: 1.6;
}
h1 { color: #007bff; border-bottom: 3px solid #007bff; padding-bottom: 10px; }
h2 { color: #28a745; border-left: 4px solid #28a745; padding-left: 15px; margin-top: 30px; }
.demo-box {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 15px 0;
}
pre {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
}
.keyword { color: #569cd6; }
.string { color: #ce9178; }
.number { color: #b5cea8; }
.function { color: #dcdcaa; }
.comment { color: #6a9955; }
.boolean { color: #569cd6; }
.class-name { color: #4ec9b0; }
#output {
background: #e9ecef;
padding: 15px;
border-radius: 8px;
margin-top: 15px;
min-height: 40px;
font-family: 'Courier New', monospace;
font-size: 0.85rem;
border-left: 4px solid #007bff;
white-space: pre-wrap;
}
table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
th, td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
th { background: #007bff; color: white; }
tr:nth-child(even) { background: #f8f9fa; }
.btn {
padding: 10px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 6px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
margin: 5px;
transition: all 0.3s;
}
.btn:hover {
background: #0056b3;
transform: translateY(-2px);
}
.btn-success { background: #28a745; }
.btn-success:hover { background: #1e7e34; }
.class-display {
font-family: 'Courier New', monospace;
font-size: 1em;
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
white-space: pre-wrap;
line-height: 1.8;
}
.input-group {
margin: 10px 0;
}
.input-group label {
display: inline-block;
min-width: 120px;
font-weight: bold;
}
.input-group input {
padding: 8px 12px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 1em;
width: 150px;
}
.input-group input:focus {
outline: none;
border-color: #007bff;
}
</style>
</head>
<body>
<h1>Iterator Interface</h1>
<div class="demo-box">
<h2>1. Manual Iteration</h2>
<pre>
<span class="keyword">const</span> arr = [<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>];
<span class="keyword">const</span> iterator = arr[<span class="function">Symbol.iterator</span>]();
console.log(iterator.<span class="function">next</span>()); <span class="comment">// { value: 10, done: false }</span>
console.log(iterator.<span class="function">next</span>()); <span class="comment">// { value: 20, done: false }</span>
console.log(iterator.<span class="function">next</span>()); <span class="comment">// { value: 30, done: false }</span>
console.log(iterator.<span class="function">next</span>()); <span class="comment">// { value: undefined, done: true }</span>
</pre>
</div>
<div class="demo-box">
<h2>2. Iterator Protocol</h2>
<pre>
<span class="keyword">const</span> myIterator = {
next() {
<span class="keyword">return</span> { value: <span class="number">1</span>, done: <span class="boolean">false</span> };
}
};
<span class="keyword">const</span> myIterable = {
[<span class="function">Symbol.iterator</span>]() {
<span class="keyword">return</span> myIterator;
}
};
</pre>
</div>
<div class="demo-box">
<h2>3. Built-in Iterables</h2>
<table>
<tr>
<th>Object</th>
<th>Iterable?</th>
<th>Yields</th>
</tr>
<tr><td>Array</td><td>✅</td><td>Values</td></tr>
<tr><td>String</td><td>✅</td><td>Characters</td></tr>
<tr><td>Map</td><td>✅</td><td>[key, value] pairs</td></tr>
<tr><td>Set</td><td>✅</td><td>Values</td></tr>
<tr><td>NodeList</td><td>✅</td><td>DOM nodes</td></tr>
<tr><td>arguments</td><td>✅</td><td>Arguments</td></tr>
<tr><td>Generator</td><td>✅</td><td>Yielded values</td></tr>
<tr><td>Plain Object {}</td><td>❌</td><td>—</td></tr>
</table>
</div>
<div class="demo-box">
<h2>4. Interactive: Custom Range Iterator</h2>
<div class="input-group">
<label for="startInput">Start:</label>
<input type="number" id="startInput" value="1">
</div>
<div class="input-group">
<label for="endInput">End:</label>
<input type="number" id="endInput" value="5">
</div>
<div style="margin: 10px 0;">
<button class="btn btn-success" onclick="iterateRange()">Iterate (for...of)</button>
<button class="btn" onclick="spreadRange()">Spread [...range]</button>
<button class="btn" onclick="destructureRange()">Destructure [a, b]</button>
<button class="btn" onclick="manualIterate()">Manual next()</button>
</div>
<div class="class-display" id="rangeDisplay">Click a button to test the Range iterator</div>
</div>
<div class="demo-box">
<h2>5. Interactive: Fibonacci Generator</h2>
<div class="input-group">
<label for="fibCount">How many?</label>
<input type="number" id="fibCount" value="10" min="1" max="30">
</div>
<div style="margin: 10px 0;">
<button class="btn btn-success" onclick="generateFibonacci()">Generate</button>
<button class="btn" onclick="fibToArray()">As Array</button>
</div>
<div class="class-display" id="fibDisplay">Click "Generate" to see Fibonacci numbers</div>
</div>
<div class="demo-box">
<h2>6. Live Output — All Examples</h2>
<div id="output">Loading...</div>
</div>
<script>
// ============================================
// Iterator Interface — Live Demo
// ============================================
let results = [];
// 1. Manual iteration
results.push('📌 Manual Iteration:\n');
const arr = [10, 20, 30];
const iterator = arr[Symbol.iterator]();
results.push(' const iterator = [10, 20, 30][Symbol.iterator]()');
results.push(' iterator.next() → ' + JSON.stringify(iterator.next()));
results.push(' iterator.next() → ' + JSON.stringify(iterator.next()));
results.push(' iterator.next() → ' + JSON.stringify(iterator.next()));
results.push(' iterator.next() → ' + JSON.stringify(iterator.next()));
results.push(' → After done: true, the iterator is exhausted');
results.push('');
// 2. Iterating through different structures
results.push('📌 Iterating Different Structures:\n');
// Array
let arrValues = [];
for (const v of [1, 2, 3]) arrValues.push(v);
results.push(' Array: [1, 2, 3] → [' + arrValues.join(', ') + ']');
// String
let strChars = [];
for (const c of "abc") strChars.push(c);
results.push(' String: "abc" → [' + strChars.join(', ') + ']');
// Map
let mapEntries = [];
for (const [k, v] of new Map([['a', 1], ['b', 2]])) {
mapEntries.push(`${k}=${v}`);
}
results.push(' Map: Map([a→1, b→2]) → [' + mapEntries.join(', ') + ']');
// Set
let setValues = [];
for (const v of new Set([1, 2, 2, 3, 3])) setValues.push(v);
results.push(' Set: Set([1, 2, 2, 3, 3]) → [' + setValues.join(', ') + '] (duplicates removed)');
results.push('');
// 3. Custom iterable — Range
results.push('📌 Custom Iterable — Range:\n');
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
}
return { value: undefined, done: true };
}
};
}
}
const range = new Range(1, 5);
results.push(' const range = new Range(1, 5)');
let rangeValues = [];
for (const n of range) rangeValues.push(n);
results.push(' for...of: [...range] → [' + rangeValues.join(', ') + ']');
results.push(' Spread: [...range] → [' + [...range].join(', ') + ']');
const [r1, r2, r3] = new Range(10, 20);
results.push(' Destructure: const [a, b, c] = new Range(10, 20)');
results.push(' → a=' + r1 + ', b=' + r2 + ', c=' + r3);
results.push('');
// 4. Custom iterable — Countdown
results.push('📌 Custom Iterable — Countdown:\n');
class Countdown {
constructor(from) {
this.from = from;
}
[Symbol.iterator]() {
let current = this.from;
return {
next: () => {
if (current >= 0) {
return { value: current--, done: false };
}
return { value: undefined, done: true };
}
};
}
}
const countdown = new Countdown(3);
results.push(' const countdown = new Countdown(3)');
results.push(' [...countdown] → [' + [...countdown].join(', ') + ']');
results.push('');
// 5. Generator version — much simpler!
results.push('📌 Generator Version (Simpler):\n');
function* rangeGen(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
results.push(' function* rangeGen(start, end) {');
results.push(' for (let i = start; i <= end; i++) yield i;');
results.push(' }');
results.push(' [...rangeGen(1, 5)] → [' + [...rangeGen(1, 5)].join(', ') + ']');
results.push(' → Generators implement Symbol.iterator automatically!');
results.push('');
// 6. Fibonacci generator
results.push('📌 Fibonacci Generator:\n');
function* fibonacci(limit) {
let a = 0, b = 1;
for (let i = 0; i < limit; i++) {
yield a;
[a, b] = [b, a + b];
}
}
results.push(' function* fibonacci(limit) {');
results.push(' let a = 0, b = 1;');
results.push(' for (let i = 0; i < limit; i++) {');
results.push(' yield a;');
results.push(' [a, b] = [b, a + b];');
results.push(' }');
results.push(' }');
results.push('');
results.push(' [...fibonacci(10)] → [' + [...fibonacci(10)].join(', ') + ']');
results.push('');
// 7. Iterator is one-time use
results.push('📌 Iterator vs Iterable:\n');
const arr2 = [1, 2, 3];
const iter = arr2[Symbol.iterator]();
results.push(' const arr = [1, 2, 3] (Iterable)');
results.push(' const iter = arr[Symbol.iterator]() (Iterator)');
results.push('');
let firstIter = [];
for (const x of iter) firstIter.push(x);
results.push(' First iteration of iter: [' + firstIter.join(', ') + ']');
let secondIter = [];
for (const x of iter) secondIter.push(x);
results.push(' Second iteration of iter: [' + secondIter.join(', ') + '] (empty — exhausted!)');
results.push('');
let arrIter1 = [];
for (const x of arr2) arrIter1.push(x);
let arrIter2 = [];
for (const x of arr2) arrIter2.push(x);
results.push(' First iteration of arr: [' + arrIter1.join(', ') + ']');
results.push(' Second iteration of arr: [' + arrIter2.join(', ') + '] (works — new iterator each time!)');
results.push('');
// 8. Where iterators are used
results.push('📌 Where Iterators Are Used:\n');
const numRange = rangeGen(1, 5);
results.push(' for...of → uses Symbol.iterator');
results.push(' Spread [...x] → uses Symbol.iterator');
results.push(' Destructuring → uses Symbol.iterator');
results.push(' Array.from(x) → uses Symbol.iterator');
results.push(' new Map(x) → uses Symbol.iterator');
results.push(' new Set(x) → uses Symbol.iterator');
results.push(' Promise.all(x) → uses Symbol.iterator');
results.push(' yield* in generator → uses Symbol.iterator');
results.push('');
// 9. Practical: custom iterable collection
results.push('📌 Practical: Iterable Playlist:\n');
class Playlist {
constructor(name, songs) {
this.name = name;
this.songs = songs;
}
[Symbol.iterator]() {
let index = 0;
const songs = this.songs;
return {
next() {
if (index < songs.length) {
return { value: songs[index++], done: false };
}
return { value: undefined, done: true };
}
};
}
// Bonus: use generator for cleaner code
*[Symbol.iterator]() {
for (const song of this.songs) {
yield song;
}
}
}
const playlist = new Playlist('Road Trip', [
'Highway to Hell',
'Born to Run',
'Don\'t Stop Believin\''
]);
results.push(' const playlist = new Playlist("Road Trip", [...])');
results.push(' Iterating through the playlist:');
for (const song of playlist) {
results.push(' 🎵 ' + song);
}
results.push('');
results.push(' Spread: [...playlist] → ' + JSON.stringify([...playlist]));
results.push(' Count: ' + [...playlist].length + ' songs');
document.getElementById('output').textContent = results.join('\n');
// ============================================
// Interactive: Range Iterator
// ============================================
function getRangeInputs() {
return {
start: Number(document.getElementById('startInput').value),
end: Number(document.getElementById('endInput').value)
};
}
function iterateRange() {
const { start, end } = getRangeInputs();
const range = new Range(start, end);
const values = [];
for (const n of range) values.push(n);
document.getElementById('rangeDisplay').textContent =
`for...of Iteration:\n` +
`─────────────────────\n` +
`Range(${start}, ${end})\n` +
`\n` +
`Values: [${values.join(', ')}]\n` +
`Count: ${values.length}`;
}
function spreadRange() {
const { start, end } = getRangeInputs();
const range = new Range(start, end);
const values = [...range];
document.getElementById('rangeDisplay').textContent =
`Spread Operator:\n` +
`─────────────────────\n` +
`[...new Range(${start}, ${end})]\n` +
`\n` +
`Result: [${values.join(', ')}]\n` +
`\n` +
`→ Spread uses Symbol.iterator automatically!`;
}
function destructureRange() {
const { start, end } = getRangeInputs();
const [a, b, c, ...rest] = new Range(start, end);
document.getElementById('rangeDisplay').textContent =
`Destructuring:\n` +
`─────────────────────\n` +
`const [a, b, c, ...rest] = new Range(${start}, ${end})\n` +
`\n` +
`a = ${a}\n` +
`b = ${b}\n` +
`c = ${c}\n` +
`rest = [${rest.join(', ')}]\n` +
`\n` +
`→ Destructuring uses Symbol.iterator!`;
}
function manualIterate() {
const { start, end } = getRangeInputs();
const iterator = new Range(start, end)[Symbol.iterator]();
let text = `Manual next() Calls:\n`;
text += `─────────────────────\n`;
for (let i = 0; i < 4; i++) {
const result = iterator.next();
text += `iterator.next() → ${JSON.stringify(result)}\n`;
}
text += `\n→ After done: true, iteration is complete`;
document.getElementById('rangeDisplay').textContent = text;
}
// ============================================
// Interactive: Fibonacci Generator
// ============================================
function* fibGen(limit) {
let a = 0, b = 1;
for (let i = 0; i < limit; i++) {
yield a;
[a, b] = [b, a + b];
}
}
function generateFibonacci() {
const count = Number(document.getElementById('fibCount').value);
const display = document.getElementById('fibDisplay');
let text = `Fibonacci Sequence (${count} numbers):\n`;
text += `─────────────────────\n`;
let i = 1;
for (const n of fibGen(count)) {
text += `${i}. ${n}\n`;
i++;
}
display.textContent = text;
}
function fibToArray() {
const count = Number(document.getElementById('fibCount').value);
const values = [...fibGen(count)];
document.getElementById('fibDisplay').textContent =
`Fibonacci as Array:\n` +
`─────────────────────\n` +
`const fib = [...fibGen(${count})]\n` +
`\n` +
`[${values.join(', ')}]\n` +
`\n` +
`Length: ${values.length}\n` +
`Sum: ${values.reduce((a, b) => a + b, 0)}`;
}
</script>
</body>
</html>
Quick Reference
Iterator Protocol
| Requirement | Description |
|---|---|
next() method | Returns { value, done } |
value | Current value |
done | true when finished |
Iterable Protocol
| Requirement | Description |
|---|---|
Symbol.iterator method | Returns an iterator |
| Called once per iteration | Each for...of gets a fresh iterator |
Built-in Iterables
| Object | Yields |
|---|---|
| Array | Values |
| String | Characters |
| Map | [key, value] pairs |
| Set | Unique values |
| NodeList | DOM nodes |
arguments | Arguments |
| Generator | Yielded values |
| TypedArray | Numbers |
| Plain Object | ❌ Not iterable |
Where Iterators Are Used
| Feature | Example |
|---|---|
for...of | for (const x of arr) |
| Spread | [...arr] |
| Destructuring | const [a, b] = arr |
Array.from() | Array.from(iterable) |
new Map() / new Set() | new Set(iterable) |
Promise.all() | Promise.all(iterable) |
yield* | yield* iterable |
Best Practices
✅ Do This:
// Use generators for clean, readable iterables
function* range(start, end) {
for (let i = start; i <= end; i++) yield i;
}
// Implement Symbol.iterator for custom objects
class MyCollection {
[Symbol.iterator]() { /* ... */ }
}
// Return a fresh iterator each time
[Symbol.iterator]() {
let index = 0;
return { next() { /* ... */ } };
}
// Use iterables with array methods
const doubled = [...range(1, 5)].map(n => n * 2);
// Prefer generators over manual iterators
// Generators are much shorter and less error-prone
❌ Don’t Do This:
// Don't reuse iterators — they're one-time use
const iter = arr[Symbol.iterator]();
for (const x of iter) { } // ✅ Works
for (const x of iter) { } // ❌ Empty — exhausted!
// Don't forget to return { done: true } eventually
// → Infinite loops!
next() {
return { value: 1, done: false }; // ❌ Never done!
}
// Don't iterate plain objects directly
const obj = { a: 1 };
for (const x of obj) { } // ❌ TypeError: obj is not iterable
// Use: for (const [k, v] of Object.entries(obj))
// Don't confuse iterable with iterator
// Iterable → has Symbol.iterator, can iterate many times
// Iterator → has next(), can only be consumed once
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
| Reusing an iterator | Second iteration is empty | Call Symbol.iterator again |
| Iterating plain objects | TypeError | Use Object.entries/keys/values |
Forgetting done: true | Infinite loop | Always return done eventually |
| Confusing iterable/iterator | Wrong assumptions | Iterable → can iterate many times |
| Not using generators | Verbose iterators | Use function* for simplicity |
Iterable vs Iterator — Visual
┌─────────────────────────────────────┐
│ ITERABLE (Array) │
│ │
│ [1, 2, 3] │
│ └── Symbol.iterator() ────┐ │
└────────────────────────────┼────────┘
│
▼
┌───────────────────────────┐
│ ITERATOR │
│ ┌─────────────────────┐ │
│ │ next() → { value } │ │
│ └─────────────────────┘ │
└───────────────────────────┘
Iterable: Has Symbol.iterator → can iterate many times
Iterator: Has next() → stateful, consumed once
When to Use What
| Goal | Use |
|---|---|
| Simple sequence | Generator function |
| Custom collection | Symbol.iterator method |
| Lazy evaluation | Generator with yield |
| Infinite sequences | Generator (never done) |
| Reverse iteration | Custom iterator |
| Multiple values at once | Generator with yield* |
Pro Tip: The iterator interface is one of the most powerful features of modern JavaScript. It’s the unifying protocol behind for...of, spread, destructuring, and many built-in methods.
Key points:
- Iterator = object with
next()→ returns{ value, done } - Iterable = object with
Symbol.iterator→ returns an iterator for...ofuses iterators automatically- Generators (
function*) implement iterators for free - Custom objects can become iterable by implementing
Symbol.iterator
When to use:
- Make your own collections iterable for
for...of - Use generators for lazy sequences (compute on demand)
- Use generators for infinite sequences
- Use
Symbol.iteratorfor custom traversal logic
The golden rule: If you want your object to work with for...of, spread, or destructuring — implement Symbol.iterator. And if you just need a quick iterable, use a generator — it’s almost always simpler than writing an iterator manually!
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!