Altcademy - a Forbes magazine logo Best Coding Bootcamp 2023

What are Iterators in JavaScript?

JavaScript is a language known for its ability to provide seamless interaction and dynamic content on web pages. One of the key features that make this possible is the concept of iterators. In this blog post, we will explore iterators in JavaScript, understand how they work, and learn to use them effectively.

What is an iterator?

An iterator is an object that enables us to traverse through a collection of data, such as an array or a list, one element at a time. It provides a consistent way to access and manipulate elements in these collections without having to know the underlying structure or implementation. Think of an iterator like a train conductor, who helps you navigate through a train, from one car to another, ensuring you reach your destination.

How do iterators work?

Iterators work by implementing a specific interface, which consists of a next method. This method returns an object with two properties: value and done. The value property contains the current element in the collection, while the done property is a boolean indicating whether the end of the collection has been reached or not. When the iterator reaches the end, the done property is set to true, indicating that there are no more elements to iterate.

Here's a simple example of an iterator for an array:

const myArray = [1, 2, 3];
const arrayIterator = myArray[Symbol.iterator]();

console.log(arrayIterator.next()); // {value: 1, done: false}
console.log(arrayIterator.next()); // {value: 2, done: false}
console.log(arrayIterator.next()); // {value: 3, done: false}
console.log(arrayIterator.next()); // {value: undefined, done: true}

In this example, we create an iterator for an array called myArray. We then use the next method to access the elements in the array one by one. When the iterator reaches the end of the array, the next method returns an object with value set to undefined and done set to true.

Using iterators with for...of loops

One of the most common use cases for iterators is to traverse through a collection using a for...of loop. The for...of loop automatically calls the next method on the iterator and assigns the value property to a variable in each iteration. The loop continues until the done property is true.

Here's an example of using a for...of loop with an array:

const myArray = [1, 2, 3];

for (const value of myArray) {
  console.log(value); // 1, 2, 3
}

In this example, the for...of loop iterates through the elements in the myArray array and logs each value to the console. Behind the scenes, the loop is using the iterator, calling the next method, and checking the done property for us.

Creating custom iterators

We can also create our own custom iterators by implementing the iterator interface. To do this, we need to define a next method inside an object and return an object with value and done properties. Additionally, we need to define a [Symbol.iterator] method that returns the iterator object.

Here's an example of a custom iterator that generates a sequence of numbers:

const numberGenerator = {
  current: 1,
  end: 5,

  [Symbol.iterator]() {
    return this;
  },

  next() {
    if (this.current <= this.end) {
      return {value: this.current++, done: false};
    } else {
      return {value: undefined, done: true};
    }
  }
};

for (const number of numberGenerator) {
  console.log(number); // 1, 2, 3, 4, 5
}

In this example, we create a custom iterator called numberGenerator that generates a sequence of numbers from 1 to 5. The iterator has a current property, representing the current number in the sequence, and an end property, representing the end of the sequence. We implement the [Symbol.iterator] method to return the iterator object and the next method to generate the next number in the sequence. Finally, we use a for...of loop to iterate through the sequence and log each number to the console.

Working with generator functions

Another way to create custom iterators is by using generator functions. A generator function is a special type of function that uses the function* keyword and the yield keyword to create an iterator. The yield keyword is used to define the value property of the iterator object and pause the execution of the function until the next method is called again.

Here's an example of a generator function that generates a sequence of numbers:

function* numberGenerator(end) {
  let current = 1;

  while (current <= end) {
    yield current++;
  }
}

const iterator = numberGenerator(5);

for (const number of iterator) {
  console.log(number); // 1, 2, 3, 4, 5
}

In this example, we create a generator function called numberGenerator that takes an end parameter and generates a sequence of numbers from 1 to the specified end value. Inside the function, we use a while loop and the yield keyword to generate the numbers. We then create an iterator by calling the generator function and passing the end value. Finally, we use a for...of loop to iterate through the sequence and log each number to the console.

Final thoughts

Iterators are a powerful feature in JavaScript that allows us to traverse through collections of data in a consistent and efficient way. By understanding how iterators work and how to create custom iterators using the iterator interface or generator functions, we can unlock the full potential of this feature and write more expressive and maintainable code. Happy coding!