DEV Community

Dharan Ganesan
Dharan Ganesan

Posted on

Day 21: Iterators

🤔 What are Iterators?

Iterators allow us to loop through data one element at a time. They provide a standardized way to access elements in a collection, such as arrays or maps, without exposing the underlying implementation details.

📜 The Iterator Protocol

Before ES6 (ECMAScript 2015), iterating over data structures in JavaScript was mainly done using for loops. However, with the introduction of the Iterator Protocol, a cleaner and more versatile approach emerged.

The Iterator Protocol consists of two essential components:

  1. Iterable: An object that has an iterator method. This method returns an Iterator object, allowing us to traverse through the collection.

  2. Iterator: An object that implements the next() method. This method returns an object with two properties: value, which represents the current element, and done, a boolean indicating whether the iteration is complete.

🛠️ Using Iterators

🔄 The for...of Loop

The for...of loop is a handy feature that emerged with ES6, making it easier to work with iterators. Let's see how it works with an array:

const fruits = ['🍎', '🍌', '🍇', '🍊', '🍓']; for (const fruit of fruits) { console.log(fruit); } 
Enter fullscreen mode Exit fullscreen mode

Here, the for...of loop automatically handles the iteration process using the iterator of the fruits array.

🎨 Creating Custom Iterators

We can also define custom iterators for our own objects. To do that, we need to implement the iterator protocol on our object.

const range = { start: 1, end: 5, [Symbol.iterator]() { let currentValue = this.start; return { next: () => { if (currentValue <= this.end) { return { value: currentValue++, done: false }; } else { return { done: true }; } }, }; }, }; for (const num of range) { console.log(num); } 
Enter fullscreen mode Exit fullscreen mode

This code will output the numbers from 1 to 5, demonstrating how we can define custom iteration behavior.

Built-in Iterators 🛠️

JavaScript provides several built-in data structures that are iterable, including arrays, strings, maps, sets, and more. We can use these structures with the for...of loop or any other iterator-consuming method.

🔤 Iterating Over a String

const message = "Hello, Dev.to! 🚀"; for (const char of message) { console.log(char); } 
Enter fullscreen mode Exit fullscreen mode

🗺️ Iterating Over a Map

const myMap = new Map([ ['key1', 'value1'], ['key2', 'value2'], ['key3', 'value3'], ]); for (const [key, value] of myMap) { console.log(`${key} => ${value}`); } 
Enter fullscreen mode Exit fullscreen mode

✨ Advantages of Using Iterators

  • Custom Iteration Behavior: With iterators, you can define custom iteration behavior for your objects. This flexibility allows you to loop through data structures in a way that makes the most sense for your specific use case.
  • Asynchronous Iteration: ES6 introduced asynchronous iterators (Symbol.asyncIterator), which are invaluable when dealing with asynchronous operations and streams. They enable elegant handling of async data.
  • Lazy Evaluation: Iterators facilitate lazy evaluation, where the next value is generated only when needed. This can be especially helpful when dealing with large data sets, as it minimizes memory consumption.
  • Generators: Generators are a special type of iterator that allows you to pause and resume the execution of a function. They are handy for creating iterators with complex logic in a more readable and maintainable way.

Top comments (0)