Learning ES6: for-of loop

So far in our Learning ES6 series, we’ve covered syntactic sugar features added with ECMAScript 6 (destructuring, rest & spread operators, and default parameters to name a few). But ES6 isn’t solely syntactic sugar. There’s new functionality as well. Some of these new spicy features are pretty advanced (like generators), but let’s start slow looking at the new for-of loop.

TL;DR

The new for-of loop introduced with ES6 allows for iterating over an array (or any iterable) in a succinct fashion similar to how we can iterate over the keys of an object using for-in.

[js]
let list = [8, 3, 11, 9, 6];

for (let value of list) {
 console.log(value);
}
[/js]

For the most part, there’s nothing really too earth-shattering here. We just now get the functionality that exists in other programming languages and has been sorely lacking in JavaScript. But keep on reading to learn the ins and outs!

ES3: for loop

Over the last two decades of JavaScript, developers have iterated over elements of an array using the simple for loop.

[js]
var list = [8, 3, 11, 9, 6];

for (var i = 0; i < list.length; i++) {
 console.log(list[i]);
}
[/js]

There’s nothing wrong with this approach, but it just feels like a lot of code to write these days. We have to keep track of the loop counter variable (i in the above example), tell it to increment by 1 with each iteration (i++), control when the iteration ends (i < list.length), and finally index into the array to get the value (list[i]).

But if we wanted to be totally accurate, we should write the code like this:

[js]
var list = [8, 3, 11, 9, 6],
 length = list.length,
 i;

for (i = 0; i < length; i++) {
 console.log(list[i]);
}
[/js]

This is because the JavaScript engine will actually hoist i to the top of the function (see the article on block-level scoping for more on variable hoisting). Also in the previous for loop, list.length gets accessed with every iteration of the loop even though it doesn’t change, so storing it in a length variable is a tad bit more efficient. This all seems overkill when all we want to do is iterate over each element of the list.

The for-in temptation

If you’ve used other programming languages such as Python, you may be tempted to use for-in with arrays for a more succinct syntax:

[js]
var list = [8, 3, 11, 9, 6],
 i;

// DON’T DO THIS!!!!
for (i in list) {
 console.log(list[i]);
}
[/js]

But you SHOULD NOT DO THIS! Depending on the JavaScript engine, it could iterate in an arbitrary order. Furthermore, other properties on the array (such as length) could also be included in the iteration (older version of Internet Explorer did this). Finally, the i iteration variable is a String, not a Number so if you try to do any math with the variable (such as i+1), you’ll actually be preforming string concatenation instead of addition.

for-in was exclusively intended for iterating over the enumerable keys of an object, and is not for iterating over arrays.

ES5: forEach method

ECMAScript 5 introduced the .forEach() instance method on arrays:

[js]
var list = [8, 3, 11, 9, 6];

list.forEach(function(value, i)) {
 console.log(value);
};
[/js]

The syntax feels less verbose because we no longer need to maintain the loop control variable and the method automatically runs to completion of the array for us. But using forEach has some unfortunate drawbacks.

With a normal for loop you can break to end the loop early. There isn’t a way to end forEach early. Including break within the forEach callback function will be a syntax error. It is only valid within loops.

Similarly, with a for loop when we return, we are exiting out of the entire function that the for loop is in. However, putting a return within the forEach callback function just exits out of the callback function itself early. It’s actually more or less equivalent to doing continue in a for loop, but far less intuitive. Including continue in the forEach callback function would be the same sort of syntax error we got with break.

Lastly, only Array objects have the .forEach() method. Other array-like objects (such as arguments or DOM NodeList) unfortunately do not, leaving us to do “clever” tricks with [].forEach.call().

ES6: for-of loop

With the new for-of loop, ES6 aims to bring the best parts from all three of the previous approaches.

[js]
let list = [8, 3, 11, 9, 6];

for (let value of list) {
 console.log(value);
}
[/js]

That’s it! We get the succinct syntax of for-in, the run-to-completion of forEach, and the ability to break, continue, and return of the simple for loop. Now JavaScript has a loop control structure that is just as succinct as what you will find in Python, C# or Java.

Remember: for-of is for iterating over arrays and for-in is for iterating over the keys of objects.

Why a new operator?

So why the new for-of operator? Why not just make for-in work correctly with arrays? They look almost identical, right? Great questions!

One of the primary objectives for the TC39 committee when coming up with new ECMAScript features is maintaining backwards compatibility. There are trillions of lines of JavaScript code out there in the Wild Wild Web, and new versions of JavaScript cannot break that code.

There are probably thousands (if not millions) of lines of JavaScript code that are intentionally or unintentionally relying on the brokenness of how for-in works with arrays. Fixing for-in to work how for-of now works would have the side effect of breaking all of that code. So unfortunately we need a new operator.

for-of and iterables

But for-of doesn’t just work for arrays. If it did, it probably wouldn’t have been meaty enough to add to the ES6 specification. Other existing collections like the DOM NodeList object, the arguments object, and String objects also work with for-of. Just like with arrays, this makes it a little bit easier to iterate over these non-Array sequences.

When you use for-of with a String, you iterate over each character in the string:

[js]
for (var char of ‘Hello’) {
 console.log(char);
}

// output:
// H
// e
// l
// l
// o
[/js]

ES6 also introduces some new collections (Map, Set, WeakMap, and WeakSet), which we’ll discuss in detail soon. These work with for-of as well. Essentially for-of can iterate over any object that is iterable. That’s where the power and worth of for-of really come through.

An object is iterable if it implements a default iterator. This probably won’t make too much sense at the moment, but we’ll learn all about iterators and iterables a little bit later. But for now it’s enough to know that you can use for-of with any native JavaScript sequence (like Array, DOM NodeList, String, arguments, etc.).

JavaScript engine support

According to the ECMAScript 6 Compatibility table all major & modern JavaScript engines (transpilers, browsers and servers) support the for-of operator.

Additional resources

Normally there would be a separate code examples page where you would find all the code used in an article, but there was hardly any code used in this article. for-of is pretty straightforward. That’s probably also why there aren’t any for-of-specific ES6 Katas either.

The for-of operator, iterators & iterables are heavily tied together, so there aren’t any blogs or books that talk about for-of separately like I’ve done here. for-of can be used without custom iterators and most developers will just use it with arrays as described above, so I felt that it was useful as a standalone topic. It is pretty simple, but if you’d like to read some more:

Coming up next…

Let’s take a look at template literals and tagged templates next. We’re back to looking at syntactic sugar again, but it’s another feature that will help you write more concise code. You’ll never concatenate strings again!

Until then…

FYI

This Learning ES6 series is actually a cross-posting of a series with the same name on my personal blog, benmvp.com. The content is pretty much the exact same except that this series will have additional information on how we are specifically leveraging ES6 here in Eventbrite Engineering when applicable. I’ll also be tackling the features in a different order than I did in my personal blog. The original for-of loop blog post can be found here.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.