JavaScript Iterators - Introduction

Chapter 11 10 mins

Learning outcomes:

  1. Introduction to iteration
  2. The for...of loop
  3. Introduction to iterators and iterables

Introduction

Iteration has always been a common concern of computer programming.

We've got tons of algorithms out there - such as those for sorting, searching, encoding, string manipulation, cryptography, graph traversal, compilers, interpreters - that require iteration at the core level to achieve their desired purposes.

Well, the list of algorithms that utilise iteration goes onto infinity. Yes, that's right - infinity!

The concept of iteration sits right at the heart of computer automation.

It is such an elementary concept that sometimes we don't even realise how many times we utilise its essence in our programs. OK, let's accept it - we've all written code at some point in our programming journeys to iterate over arrays!

There is no programming language out there that doesn't facilitate iteration.

You name C, C++, Java, Python, PHP, Ruby, Perl, Swift; even JavaScript - all languages provide some sort of keywords and statements to implement iteration.

The most common and widely known categories are the for and while loops.

for is mostly used to iterate over sequences/collections, where a counter is used to keep track of all the elements in the sequence; whereas while is mostly used to iterate uptil the point some condition doesn't fail.

However, there is absolutely no problem in using one in place of the other - it's just a matter of convention and common sense to use for for iterating a known number of times and while for iterating an unknown number of times!

Now, if we zoom into the for loop a bit, we see that it's pretty convenient for the purpose of iterating over a given sequence, or collection.

For example consider the code below. We have an array nums, iterated over using the for loop with the help of the counter variable i:

var nums = [1, 5, 16];

for (var i = 0; i < nums.length; i++) {
   console.log(nums[i]);
}
1
5
16

See how natural this code feels both to write and read. Regardless, still it's got an annoying boilerplate: first declaring, then checking, then incrementing, and finally writing the iterative statements!

This is just a lot of work!

Can't there be a simpler construct that does all this for us, under the hood?

This was exactly the question developers of JavaScript asked themselves. But since they were the developers of the language, unlike us, they actually gave such a construct!

Simply, wow!

And thus we got the for...of loop.

The for...of loop

The for...of loop makes iteration over a sequence superbly easy.

Just specify the sequence inside the loop's header, directly or as an identifier, and then wait for the loop to show its power.

Here's how a general for...of loop looks:

for (var someVar of sequence) {
   // loop body
}

We start with the for keyword, then inside the loop's header, create a variable which is followed by the of keyword and finally by the sequence to go over.

Although it's not necessary to declare a variable someVar as well inside the loop's header, we usually do so to keep the code concise.

However, remember that someVar can't be omitted completely - it must be there, otherwise an error will be thrown.

Anyways, let's now apply this to the array iteration example above:

var nums = [1, 5, 16];

for (var num of nums) {
   console.log(num);
}

Inside the loop's header, first we declare a variable num and then set the array nums as the sequence it'll get its values from.

1
5
16

Now what happens behind the scenes here is that each value of nums is extracted, starting at index 0, and then assigned to the variable num before executing the loop's body.

This is quite synonymous with the code shown below:

var nums = [1, 5, 16];

for (var i = 0; i < nums.length; i++) {
var num = nums[i];
   console.log(num);
}

Note that we're just saying that this code works similar to the for...of example shown above, not that this is exactly the way it works.

The idea is the same i.e we start at some point, we end at some point; but the implementation is not the same!

But don't worry - in the next chapter we shall get a detailed discussion on the implementation part as well, when we learn the concept of iterator objects.

For now, just try to get your hands comfortable with the for...of loop by experimenting around with a couple of sequences.

Below we use the for...of in iterating over the list of p elements and logging each one's innerHTML value:

<p>Para 1</p>
<p>Para 2</p>
<p>Para 3</p>
var pList = document.getElementsByTagName("p");

for (var p of pList) {
   console.log(p.innerHTML);
}
For further information on JavaScript DOM selection methods like getElementsByTagName(), please refer to HTML DOM - Accessing Elements.
Para 1
Para 2
Para 3
As we shall see in the next chapter, using pList in the for...of loop here is possible only because pList follows the iterable protocol.

Essentially, anything that is a sequence/collection of values where some attribute of the sequence is changing continuously, can be used inside for...of.

As such we can iterate over strings, arrays (as we've just seen), sets, maps, DOMTokenList, HTMLCollection objects, typed arrays and so on and so forth.

Consider the tasks below for a quick go through.

Loop over the string str shown below using a for...of loop, and log each of its characters in their uppercased forms.

var str = "hello";
For details on how to uppercase characters in a JavaScript string please refer to Strings Properties and Methods.

Here's the solution:

var str = "hello";

for (var char of str) {
   console.log(char.toUpperCase());
}
H
E
L
L
O

Loop over the typed array uint8 shown below using a for...of loop, and log each of its byte values in hexadecimal representation.

var uint8 = new Uint8Array([15, 192, 16]);
For details on how to conver a number to hexadecimal representation please refer to JavaScript Number Methods.

Here's the solution:

var uint8 = new Uint8Array([15, 192, 16]);

for (var byte of uint8) {
   console.log(byte.toString(16));
}
f
c0
10

Iterators and iterables

The reason as to why arrays, strings, sets, maps, element lists etc. are able to work with the for...of loop is simply because they are considered iterables.

An iterable is something that can be iterated over.

Examples of iterables, as we know include arrays, strings... whereas examples of non-iterables include functions, numbers, booleans.

Does it even make sense to iterate over a number, a boolean, a function? These data types aren't a sequence, or a collection of individual values - rather they are single values in themselves.

Now being iterable doesn't always has to do with the default setup in JavaScript - one can also make a non-iterable data type iterable by implementing the iterable protocol.

The way this goes is that we define an @@iterator method on the non-iterable data type, which, when called, returns an iterator object following the iterator protocol.

An iterator is an object that performs iteration over a sequence.

The interpreter uses this iterator object to perform iteration over the given data type. In fact, this is also the way iterable data types like arrays implement iteration.

How iterables operate or how this iterator object works is a matter of precise detail, discussion and illustration; and will thereby be dealt with in the next chapters - the first one on iterators and the second one on iterables.

Just to get ourselves ready for the upcoming, let's review the distinction between an iterable and an iterator.

An iterable is something that can be iterated over while an iterator is the actual performer of the iteration.

With this distinction in mind, let's embark on the journey to explore modern iteration in JavaScript.