for-of loopfor-of loopfor-of only works with iterable valuesconst declarations versusvar declarationsfor-of is a new loop in ES6 that replaces bothfor-in andforEach() and supports the new iteration protocol.
Use it to loop overiterable objects (Arrays, strings, Maps, Sets, etc.; see Chap. “Iterables and iterators”):
constiterable=['a','b'];for(constxofiterable){console.log(x);}// Output:// a// b
break andcontinue work insidefor-of loops:
for(constxof['a','','b']){if(x.length===0)break;console.log(x);}// Output:// a
Access both elements and their indices while looping over an Array (the square brackets beforeof mean that we are usingdestructuring):
constarr=['a','b'];for(const[index,element]ofarr.entries()){console.log(`${index}.${element}`);}// Output:// 0. a// 1. b
Looping over the [key, value] entries in a Map (the square brackets beforeof mean that we are usingdestructuring):
constmap=newMap([[false,'no'],[true,'yes'],]);for(const[key,value]ofmap){console.log(`${key}=>${value}`);}// Output:// false => no// true => yes
for-of loopfor-of lets you loop over data structures that areiterable: Arrays, strings, Maps, Sets and others. How exactly iterability works is explained in Chap. “Iterables and iterators”. But you don’t have to know the details if you use thefor-of loop:
constiterable=['a','b'];for(constxofiterable){console.log(x);}// Output:// a// b
for-of goes through the items ofiterable and assigns them, one at a time, to the loop variablex, before it executes the body. The scope ofx is the loop, it only exists inside it.
You can usebreak andcontinue:
for(constxof['a','','b']){if(x.length===0)break;console.log(x);}// Output:// a
for-of combines the advantages of:
for loops:break/continue; usable in generatorsforEach() methods: concise syntaxfor-of only works with iterable valuesThe operand of theof clause must be iterable. That means that you need a helper function if you want to iterate over plain objects (see “Plain objects are not iterable”). If a value is Array-like, you can convert it to an Array viaArray.from():
// Array-like, but not iterable!constarrayLike={length:2,0:'a',1:'b'};for(constxofarrayLike){// TypeErrorconsole.log(x);}for(constxofArray.from(arrayLike)){// OKconsole.log(x);}
const declarations versusvar declarationsIf youconst-declare the iteration variable, a freshbinding (storage space) will be created for each iteration. That can be seen in the following code snippet where we save the current binding ofelem for later, via an arrow function. Afterwards, you can see that the arrow functions don’t share the same binding forelem, they each have a different one.
constarr=[];for(constelemof[0,1,2]){arr.push(()=>elem);// save `elem` for later}console.log(arr.map(f=>f()));// [0, 1, 2]// `elem` only exists inside the loop:console.log(elem);// ReferenceError: elem is not defined
Alet declaration works the same way as aconst declaration (but the bindings are mutable).
It is instructive to see how things are different if youvar-declare the iteration variable. Now all arrow functions refer to the same binding ofelem.
constarr=[];for(varelemof[0,1,2]){arr.push(()=>elem);}console.log(arr.map(f=>f()));// [2, 2, 2]// `elem` exists in the surrounding function:console.log(elem);// 2
Having one binding per iteration is very helpful whenever you create functions via a loop (e.g. to add event listeners).
You also get per-iteration bindings infor loops (vialet) andfor-in loops (viaconst orlet). Details are explained inthe chapter on variables.
So far, we have only seenfor-of with a declared iteration variable. But there are several other forms.
You can iterate with an existing variable:
letx;for(xof['a','b']){console.log(x);}
You can also iterate with an object property:
constobj={};for(obj.propof['a','b']){console.log(obj.prop);}
And you can iterate with an Array element:
constarr=[];for(arr[0]of['a','b']){console.log(arr[0]);}
Combiningfor-of with destructuring is especially useful for iterables over [key, value] pairs (encoded as Arrays). That’s what Maps are:
constmap=newMap().set(false,'no').set(true,'yes');for(const[k,v]ofmap){console.log(`key =${k}, value =${v}`);}// Output:// key = false, value = no// key = true, value = yes
Array.prototype.entries() also returns an iterable over [key, value] pairs:
constarr=['a','b','c'];for(const[k,v]ofarr.entries()){console.log(`key =${k}, value =${v}`);}// Output:// key = 0, value = a// key = 1, value = b// key = 2, value = c
Therefore,entries() gives you a way to treat iterated items differently, depending on their position:
/** Same as arr.join(', ') */functiontoString(arr){letresult='';for(const[i,elem]ofarr.entries()){if(i>0){result+=', ';}result+=String(elem);}returnresult;}
This function is used as follows:
> toString(['eeny', 'meeny', 'miny', 'moe'])'eeny, meeny, miny, moe'