logo image Faisal Rahman
ID EN
My profile picture taken in the summer Faisal Rahman

A New Way to Iterate in ES6 with Iterables and Iterators


Just as all roads lead to Rome, JavaScript also has several roads for performing iteration. Some of the most common ones we encounter include for loop, while loop, and do-while loop. Let’s look at an example of iterating over an array below.

const arr = [0, 1, 2, 3, 4, 5];

// classic for loop
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}

// classic while loop
const j = 0;
while (j < arr.length) {
  console.log(arr[j]);
  j++;
}

// do-while, somewhat less common
const k = 0;
do {
  console.log(arr[k]);
  k++;
} while (k < arr.length);

These three classic iteration approaches are often sufficient for our programs. However, ES6 brings a new and quite interesting way to iterate: for-of.

The For-of Loop

Iterating with for-of has a very concise and clean syntax. Still using the same arr array:

for (item of arr) {
  console.log(item);
}

It turns out that, beyond arrays, the for-of loop can also be used to iterate over other objects.

const str = "Hello";

for (char of str) {
  console.log(char);
} // "H", "e", "l", "l", "o"

const s = new Set(str);

for (val of s) {
  console.log(val);
} // "H", "e", "l", "o"

const m = new Map([
  [1, 2],
  [3, 4],
]);

for (val of m) {
  console.log(val);
} // [1, 2], [3, 4]

Besides arrays, this appealing iteration syntax can also be applied to String, Set, and Map objects. Why is that? Let’s look at what MDN says about for-of:

“The for...of statement creates a loop Iterating over iterable objects” - Mozilla Developer Network

It turns out there is a classification of objects called “iterable”, which includes Arrays, Strings, Sets, and Maps. If you come from Java, you might intuitively guess that iterable refers to some interface. However, JavaScript does not have interfaces — what JavaScript recognizes is protocols, and iterable is one of them.

The Iterable Protocol

Protocols in JavaScript are conventions that an object must satisfy in order to make use of certain features of the JavaScript language. The concept of protocols was introduced in ES6 with the iterable and iterator protocols. As described above, objects that satisfy the iterable protocol can harness the power of for-of.

To be considered iterable, an object (or another object in its prototype chain) must implement the @@iterator method — a function that returns an iterator object. That function must be attached to the object’s Symbol.iterator property. This symbol is one of JavaScript’s built-in symbols, specifically pointing to an object’s @@iterator method.

So Arrays, Strings, Sets, and Maps all have an iterator method? That’s exactly right!

const a = [];
typeof a[Symbol.iterator]; // "function"

const s = new Set();
typeof s[Symbol.iterator]; // "function"

const m = new Map();
typeof m[Symbol.iterator]; // "function"

We can implement our own iterator object by filling the [Symbol.iterator] property with an iterator method.

const obj = { name: 'Faisal', age: 17 };

function myIterator() { // our iterator method implementation }

obj[Symbol.iterator] = myIterator;

We can also borrow an iterator method from another object (and indeed one of the benefits of this protocol is that iterators can be reused).

const obj = {
  0: "a",
  1: "b",
  2: "c",
  3: "d",
  length: 4,

  // hehehe
  [Symbol.iterator]: Array.prototype[Symbol.iterator],
};

for (value of obj) console.log(value); // "a", "b", "c", "d"

The function we attach as an iterator method to our object cannot be just any function — it must satisfy another protocol called the iterator protocol.

The Iterator Protocol

The next protocol we will explore is the iterator protocol. The iterator protocol classifies objects that can define the iteration behavior of an object. For an object to become an iterator, this protocol requires the object to implement a next function that:

Talk is confusing, show me the code:

// this function is an iterator method
function myIterator() {
  let n = 0;

  // this function returns an iterator object
  return {
    next: function () {
      n++;
      return { value: n, done: false };
    },
  };
}

const it = myIterator(); // it is an iterator object

it.next(); // { value: 1, done: false }
it.next(); // { value: 2, done: false }
it.next(); // { value: 3, done: false }

Note that this iterator will never produce a done value of true, meaning iteration with this iterator will be endless — an infinite loop. Yes, that is allowed.

Now let’s try an iterator with different behavior on an object. Say we want our iterator to make all odd numbers even in the array we have by multiplying them by 2, while leaving already-even numbers alone.

const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

function evenizingIterator() {
  let idx = 0;

  return {
    // using an arrow function so `this` is bound
    // to the context of the array it is attached to
    next: () => {
      if (idx < this.length) {
        const value = this[idx] % 2 === 0 ? this[idx] : this[idx] * 2;
        idx = idx + 1;
        return { value: value, done: false };
      } else {
        return { done: true };
      }
    },
  };
}

arr[Symbol.iterator] = evenizingIterator;
for (v of arr) console.log(v);
// 0, 2, 2, 6, 4, 10, 6, 14, 8, 18, 10

So, is making an object iterable only useful for using for-loops? Of course not — there are many more JavaScript syntaxes that consume iterables.

// Array.from
const arr2 = Array.from(arr);

// spread, rest, and destructuring assignment
const arr3 = [...arr];

const [a, b, c] = [...arr];

const [d, e, f, ...g] = [...arr];

// other examples
Promise.all(iterable);

Promise.race(iterable);

So, What Are the Benefits of Using Iterables?

First, as we saw above, iterables allow us to use several native features of JavaScript. Beyond that:


That wraps up the overview of iterables and iterators — I hope it helps power up your iterations going forward. There is actually one more related topic, namely generators which act as a factory for iterators, but since this has already gotten quite long I’ll save it for a separate post. Stay tuned!

Further reading: