Cheatsheet: JS loops (forEach, for/in, for/of)
Published: 2021-02-01 06:01:53

**tl;dr** the best use for these loops is as follows:
- arrays: use `for... of` (or `forEach`)
- objects: use `for... in`
- if you work with arrays and need an access to indexes, use `for` or `forEach`

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:

```js
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):

```js
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:
```js
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:

1. it checks whether an element is iterable (using [GetIterator](http://www.ecma-international.org/ecma-262/6.0/#sec-getiterator) operation) and throws an appropriate `TypeError` if it's not:

    ```js
    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):
    ```js
    let num = 3
    for (let i in num){
     console.log(i)
    }
    
    /// RESULT:
    // undefined
    ```

2. It supports all kinds of control flow in the loop body, like `continue`, `break`, `yield` and `await`

3. 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:

```js

////////////////// 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.

```js
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](https://www.pexels.com/)