JavaScript Development Space

Master ES6 Iterators for Clean JavaScript Code

ES6 introduced the Iterator interface, enabling sequential access to iterable objects like Arrays, Sets, Maps, Strings, arguments, and custom objects. Iterators provide a standardized way to traverse data structures and are essential for for...of loops. In this guide, you'll learn how to use ES6 iterators to write clean, efficient JavaScript code. We'll cover the basics of iterators, how to create custom iterators, and how to use generator functions to simplify iteration patterns. Let's get started!

1. What is an Iterator?

An iterator is an object with a next() method that returns:

js
1 { value: currentValue, done: true/false }
  • done: false → More values remain.
  • done: true → Iteration is complete.

Iteration Process:

  1. Create an iterator pointing to the start of the data structure.
  2. Call next() to move to the first element.
  3. Keep calling next() to traverse through elements.
  4. Stops when done: true is returned.

2. How to Create an Iterator

(1) Manually Implement an Iterator

js
1 function createIterator(arr) {
2 let index = 0;
3 return {
4 next: function () {
5 return index < arr.length
6 ? { value: arr[index++], done: false }
7 : { value: undefined, done: true };
8 }
9 };
10 }
11
12 let iterator = createIterator(["a", "b", "c"]);
13 console.log(iterator.next()); // { value: 'a', done: false }
14 console.log(iterator.next()); // { value: 'b', done: false }
15 console.log(iterator.next()); // { value: 'c', done: false }
16 console.log(iterator.next()); // { value: undefined, done: true }

📌 Each call to next() returns a value and moves forward.

(2) Use Symbol.iterator for Built-in Iterables

All iterable objects (Array, Set, Map, String) have a default iterator accessible via Symbol.iterator.

📌 Arrays, Strings, Sets, and Maps can be iterated directly.

(3) Create an Iterator for a Custom Object

Ordinary objects are not iterable by default but can be made iterable using Symbol.iterator.

js
1 let myObj = {
2 data: [10, 20, 30],
3 [Symbol.iterator]: function () {
4 let index = 0;
5 return {
6 next: () => {
7 return index < this.data.length
8 ? { value: this.data[index++], done: false }
9 : { value: undefined, done: true };
10 }
11 };
12 }
13 };
14
15 let iter = myObj[Symbol.iterator]();
16 console.log(iter.next()); // { value: 10, done: false }
17 console.log(iter.next()); // { value: 20, done: false }
18 console.log(iter.next()); // { value: 30, done: false }
19 console.log(iter.next()); // { value: undefined, done: true }

📌 Objects require Symbol.iterator to be iterable with for...of.

3. How to Use for...of for Iteration

All objects implementing Symbol.iterator can be traversed using for...of.

js
1 let arr = ["A", "B", "C"];
2 for (let char of arr) {
3 console.log(char);
4 }
5 // A
6 // B
7 // C

📌 Unlike forEach(), for...of allows break and continue.

4. What Are Iterable Objects?

✅ Objects supporting for...of via Symbol.iterator:

  • Array
  • String
  • Set
  • Map
  • arguments
  • NodeList
  • Custom objects (with Symbol.iterator implemented)

5. How to Iterate Over Set and Map

(1) Iterate Over a Set

js
1 let mySet = new Set(["apple", "banana", "cherry"]);
2 let setIter = mySet[Symbol.iterator]();
3
4 console.log(setIter.next()); // { value: 'apple', done: false }
5 console.log(setIter.next()); // { value: 'banana', done: false }
6 console.log(setIter.next()); // { value: 'cherry', done: false }
7 console.log(setIter.next()); // { value: undefined, done: true }

📌 Set maintains insertion order and ensures unique values.

(2) Iterate Over a Map

js
1 let myMap = new Map([
2 ["name", "Alice"],
3 ["age", 25]
4 ]);
5
6 for (let [key, value] of myMap) {
7 console.log(key, value);
8 }
9 // name Alice
10 // age 25

📌 Map iteration returns [key, value] pairs.

6. How Iterators Compare to Generators

The key differences between Iterators and Generators lie in their creation and execution patterns. For creation, Iterators require manual implementation of the next() function and Symbol.iterator, making them more verbose to write. Generators, on the other hand, use the function* syntax which automatically generates these implementations, significantly reducing boilerplate code.

When it comes to execution, Iterators run without pausing – they process their elements continuously until completion. In contrast, Generators support the yield keyword, allowing them to pause execution and resume later. This makes Generators particularly useful for controlling flow and managing memory in large datasets or infinite sequences.

Example: Using a Generator

js
1 function* generatorFunction() {
2 yield "A";
3 yield "B";
4 yield "C";
5 }
6
7 let gen = generatorFunction();
8 console.log(gen.next()); // { value: 'A', done: false }
9 console.log(gen.next()); // { value: 'B', done: false }
10 console.log(gen.next()); // { value: 'C', done: false }
11 console.log(gen.next()); // { value: undefined, done: true }

📌 Generators simplify iteration with yield and can pause execution.

7. Best Practices for Using Iterators

(1) Implement Iterators in a Class

js
1 class Collection {
2 constructor() {
3 this.items = [];
4 }
5
6 add(item) {
7 this.items.push(item);
8 }
9
10 *[Symbol.iterator]() {
11 for (let item of this.items) {
12 yield item;
13 }
14 }
15 }
16
17 let collection = new Collection();
18 collection.add('foo');
19 collection.add('bar');
20
21 for (let value of collection) {
22 console.log(value);
23 }
24 // foo
25 // bar

(2) Use Asynchronous Iterators (ES2018)

js
1 const asyncIterable = {
2 async *[Symbol.asyncIterator]() {
3 yield 'hello';
4 yield 'async';
5 yield 'iteration';
6 }
7 };
8
9 (async () => {
10 for await (const x of asyncIterable) {
11 console.log(x);
12 }
13 })();
14 // hello
15 // async
16 // iteration

8. When to Use Iterators

✅ Iterators are useful for:

  • Traversing arrays, strings, sets, and maps
  • Creating custom iterable objects
  • Handling streaming data (e.g., paginated API responses)
  • Avoiding memory-heavy operations by using generators

9. Conclusion

  • Iterators (Symbol.iterator) provide a structured way to access collections.
  • for...of simplifies iteration over iterable objects.
  • Generators (function*) create iterators effortlessly with yield.
  • Asynchronous Iterators (Symbol.asyncIterator) handle streaming data efficiently.

Mastering iterators will make your JavaScript code more efficient and readable! 🚀

JavaScript Development Space

© 2025 JavaScript Development Space - Master JS and NodeJS. All rights reserved.