for..of Loops

Joining the for and for..in loops from the JavaScript we’re all familiar with, ES6 adds a for..of loop, which loops over the set of values produced by an iterator.

The value you loop over with for..of must be an iterable, or it must be a value which can be coerced/boxed to an object (see the Types & Grammar title of this series) that is an iterable. An iterable is simply an object that is able to produce an iterator, which the loop then uses.

Let’s compare for..of to for..in to illustrate the difference:

  1. var a = ["a","b","c","d","e"];
  2. for (var idx in a) {
  3. console.log( idx );
  4. }
  5. // 0 1 2 3 4
  6. for (var val of a) {
  7. console.log( val );
  8. }
  9. // "a" "b" "c" "d" "e"

As you can see, for..in loops over the keys/indexes in the a array, while for..of loops over the values in a.

Here’s the pre-ES6 version of the for..of from that previous snippet:

  1. var a = ["a","b","c","d","e"],
  2. k = Object.keys( a );
  3. for (var val, i = 0; i < k.length; i++) {
  4. val = a[ k[i] ];
  5. console.log( val );
  6. }
  7. // "a" "b" "c" "d" "e"

And here’s the ES6 but non-for..of equivalent, which also gives a glimpse at manually iterating an iterator (see “Iterators” in Chapter 3):

  1. var a = ["a","b","c","d","e"];
  2. for (var val, ret, it = a[Symbol.iterator]();
  3. (ret = it.next()) && !ret.done;
  4. ) {
  5. val = ret.value;
  6. console.log( val );
  7. }
  8. // "a" "b" "c" "d" "e"

Under the covers, the for..of loop asks the iterable for an iterator (using the built-in Symbol.iterator; see “Well-Known Symbols” in Chapter 7), then it repeatedly calls the iterator and assigns its produced value to the loop iteration variable.

Standard built-in values in JavaScript that are by default iterables (or provide them) include:

  • Arrays
  • Strings
  • Generators (see Chapter 3)
  • Collections / TypedArrays (see Chapter 5)

Warning: Plain objects are not by default suitable for for..of looping. That’s because they don’t have a default iterator, which is intentional, not a mistake. However, we won’t go any further into those nuanced reasonings here. In “Iterators” in Chapter 3, we’ll see how to define iterators for our own objects, which lets for..of loop over any object to get a set of values we define.

Here’s how to loop over the characters in a primitive string:

  1. for (var c of "hello") {
  2. console.log( c );
  3. }
  4. // "h" "e" "l" "l" "o"

The "hello" primitive string value is coerced/boxed to the String object wrapper equivalent, which is an iterable by default.

In for (XYZ of ABC).., the XYZ clause can either be an assignment expression or a declaration, identical to that same clause in for and for..in loops. So you can do stuff like this:

  1. var o = {};
  2. for (o.a of [1,2,3]) {
  3. console.log( o.a );
  4. }
  5. // 1 2 3
  6. for ({x: o.a} of [ {x: 1}, {x: 2}, {x: 3} ]) {
  7. console.log( o.a );
  8. }
  9. // 1 2 3

for..of loops can be prematurely stopped, just like other loops, with break, continue, return (if in a function), and thrown exceptions. In any of these cases, the iterator’s return(..) function is automatically called (if one exists) to let the iterator perform cleanup tasks, if necessary.

Note: See “Iterators” in Chapter 3 for more complete coverage on iterables and iterators.