Day 21: Iterators
Tags: day21, javascript, 100daysofcode
Published: 2023-08-08 15:00:00


## 🤔 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:

```javascript
const fruits = ['🍎', '🍌', '🍇', '🍊', '🍓'];

for (const fruit of fruits) {
  console.log(fruit);
}
```

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.

```javascript
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);
}
```

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 

```javascript
const message = "Hello, Dev.to! 🚀";

for (const char of message) {
  console.log(char);
}
```

### 🗺️ Iterating Over a Map 


```javascript
const myMap = new Map([
  ['key1', 'value1'],
  ['key2', 'value2'],
  ['key3', 'value3'],
]);

for (const [key, value] of myMap) {
  console.log(`${key} => ${value}`);
}
```

## ✨ 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.