tl;dr the best use for these loops is as follows:
- arrays: use
for... of
(orforEach
) - objects: use
for... in
- if you work with arrays and need an access to indexes, use
for
orforEach
I remember this as A ray of sunshine and foreign objects in the sky, which reminds me that:
- Array ->
for/of
- Object ->
for/in
for... in
loop
This loop was introduced in ES6, as a tool to use with enumerables β- to iterate over the properties of the object:
let person = {name: "SpongeBob", lastName: "SquarePants", age: 34}
for (let property in person) {
console.log(`${property}: ${person[property]}`);
}
///// RESULT:
// "name: SpongeBob"
// "lastName: SquarePants"
// "age: 34"
Now, you can iterate over an array with this tool but that doesn't mean you should. It works because in JS arrays inherit from the Object
and because of that, the bracket notation ([]
) will have a similar effect if you call this looping tool on an array as it has on an object. However, there are two caveats: arrays impact on speed and situations when you're dealing with a NodeList or just inconsistent data in an array.
As for speed, the situation is clear: when iterating over arrays, for... in
is much slower than the for... of
loop that is specific to arrays, strings and NodeLists.
Now, in order to understand the issues with inconsistent data, you need to remember that Array
is an Object
and as such, you can assign to it properties (which should not happen but may):
let students = ["SpongeBob", "Patrick"]
students.name = "Mr. Krabs"
students
// ["SpongeBob", "Patrick", name: "Mr. Krabs"]
Now, this is an example of inconsistent data and we would wish that our loop would just disregard it. However, for... in
was created to iterate over properties and so it will do just that:
let students = ["SpongeBob", "Patrick"]
students.name = "Mr. Krabs"
for (let element in students){
console.log(`${element} is ${students[element]}`)
}
//// RESULTS
// "0 is SpongeBob"
// "1 is Patrick"
// "name is Mr. Krabs"
Now, this is especially important when it comes to e.g. NodeLists that look like an array but in fact also hold a bunch of other elements (e.g. methods) and it's best if they were skipped altogether.
for... of
loop
Also introduced in ES6, for... of
is specific to iterables: arrays, strings, NodeLists, sets and maps. It comes with a great set of features:
-
it checks whether an element is iterable (using GetIterator operation) and throws an appropriate
TypeError
if it's not:
let num = 3 for (let i of num){ console.log("hi") } /// RESULT: // Uncaught TypeError: num is not iterable
Compare it with
for... in
(which you should not use on arrays):
let num = 3 for (let i in num){ console.log(i) } /// RESULT: // undefined
It supports all kinds of control flow in the loop body, like
continue
,break
,yield
andawait
It's faster than
forEach
(stats will be added here :) ).
β--
forEach
Now, there's this syntactic sugar: forEach
, which takes up to three arguments:
arr.forEach(callback(currentElement, index, array) {
// do something <- this is a callback
});
which are:
-
currentElement
- the element that's being acted on (this one is the only mandatory one), -
index
- the current index of the element, -
array
- the array that's being iterated over
So, a benefit to forEach
is that you do have an access to the index, which for... of
does not give you (well, there is a lengthy way of obtaining one but it's an overkill). Also, this is a finite loop by definition and so it will end at some point, even if you don't factor in a break
statement for an edge-case situation.
There's another, more obscure benefit: the temporary variable (currentElement
in the example above) is locally-scoped, whereas it is not so in the for... of
loop. See here:
////////////////// forEach
let num = 4;
let arr = [5, 6, 7];
arr.forEach(num => {
console.log(num);
});
// returns 5, 6, 7
console.log(num)
// 4
////////////////// for... of
for(num of arr){
console.log(num)
}
// returns 5, 6, 7
console.log(num)
// 7
In the above example, we can see that forEach
has not overwritten the value of the num
variable declared outside of its scope, whereas that was not the case with for... of
loop. However, if you can't name your variables in a more descriptive way, there still is a fix for that: use let
inside of the for... of
array.
let num = 4;
let arr = [5, 6, 7];
for(let num of arr){
console.log(num)
}
// returns 5, 6, 7
console.log(num)
// 4
for
loop
This loop is just the oldest and most-widely supported for
loop in JS. You can always rely on it. It also allows you to be more intentional about which is the starting/ending element, or which direction the iteration should go.
However, the dev experience is not perfect because its syntax is not straightforward so feel free to use for... in
(when dealing with objects) or for... of
(when dealing with arrays, strings, sets, nodeLists and maps) instead where appropriate.
Photo by Stas Knop from Pexels