Loop through an array in Javascript

Iterating arrays in Javascript

For a long time, in the era of ECMAScript 3 (1999 - 2008), the for..in statement was very often misused to iterate over array objects, this statement is really meant to enumerate object properties, no question why many people reported strange behaviors. For a newbie or a foreigner of the Javascript world, coming from other languages, the for-in statement might look just good but it shouldn't be used for this.

The simplest way I long suggested was to use a plain sequential for loop:

let array = ["Hello","World"];
for (let i = 0; i < array.length; i++) {
    console.log(array[i]);
}

And this was the most common way to iterate array objects, of course you can also use any other sequential loop like while, do..while, etc...

Enter ECMAScript 5 forEach

The ECMAScript 5 Specification (ES5 for short) introduced a lot of very helpful array methods, one of them, the Array.prototype.forEach and it gives us a very short way to iterate over an array:

array.forEach(function (item) {
    console.log(item);
});

Being almost 9 years as the time of writing that the ES5 spec. was released (Dec. 2009), it has been implemented by almost all modern engines in the desktop, server and mobile environments, so it's safe to use them.

And with the ES6 arrow function syntax, it's even more succinct:

array.forEach(item => console.log(item));

Very short and sweet.

Arrow functions are also being widely implemented, unless you plan to support very old platforms (e.g. IE11) you are also safe to go.

The forEach method accepts two parameters, the first a callback function, and the second one an optional thisArg, which in case provided, will set the this value of the callback function if possible (e.g. it not possible to bind the this value of arrow functions).

The callback function passed to forEach will actually receive 3 arguments when called for each element and they are:

  • The current element
  • The index of the element
  • A reference to the array object being iterated

For example, the following:

var colors = ['red', 'green', 'blue'];

colors.forEach(function (c, i, array) {
  console.log(c, i, array);1
});

Will log:

red   0 Array(3) [ "red", "green", "blue" ]
green 1 Array(3) [ "red", "green", "blue" ]
blue  2 Array(3) [ "red", "green", "blue" ]

However there are some differences with a sequential loop, for instance, the forEach method lacks the ability to stop its execution at an arbitrary point, thing that we can easily do with the break statement. Also you cannot return from the enclosing function if you wanted, since the return value of the callback is simply ignored.

Another difference is that if we have an sparse array, non existent elements are skipped by the forEach method:

var array = [1, , , , 2];
array.forEach((el, i) => console.log(el, i));

// Will log:
// 1  0
// 2  4

Sparse arrays or arrays with holes are by definition, cases in which some of the index properties are missing from the object itself, and there is no continuos sequence from 0 to length - 1.

Enter ECMAScript 6 for-of

The ES6 standard introduces the concept of iterable objects and defined a new construct for traversing data, the for...of statement.

Iterables and generators are a big piece of ES6 that deserve to have its own article.

Array objects are by definition built-in iterables in ES6, so you can use this statement on them:

let colors = ['red', 'green', 'blue'];
for (const color of colors){
  console.log(color);
}

Since it's a ES6 statement, we can use more of its features, like let and const bindings.

The for-of statement is also widely implemented in mature platforms, so you can use it if you don't plan to target old engines.

Why not use for-in ?

Coming back to the for-in statement, it should be avoided to iterate arrays because as I said earlier, this statement is really meant to enumerate object properties.

It can give unexpected results if you use it to iterate over arrays because:

  • The order of enumeration is not guaranteed by the specification, the array indexes may not be visited in numeric order.
  • Inherited properties are also enumerated.

The second point can give you a lot of problems, for example, if the Array.prototype object is extended to include a method there, that property will be also enumerated.

For example:

// Don't do this!
Array.prototype.test = "inherited!";
var array = ['a', 'b', 'c'];
array.prop = 'prop';

for (var i in array) {
  console.log(array[i]);
}

The above code will log, "a", "b", "c" "prop" and "inherited!".

That was a big problem in the past, particularly if you used some library that relied heavily on native prototypes augmentation (such as early versions of MooTools for example).

The for-in statement as I said before is there to enumerate object properties, for example:

var obj = {
  "a": 1,
  "b": 2,
  "c": 3
};

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) { 
    // or if (Object.prototype.hasOwnProperty.call(obj,prop)) for safety...
    console.log("prop: " + prop + " value: " + obj[prop])
  }
}

In the above example the hasOwnProperty method allows you to enumerate only own properties, that's it, only the properties that the object physically has, no inherited properties.

Now with ECMAScript 5 we have the Object.keys method to get the enumerable own properties.

Conclusions

If you can target ES6, use the for-of statement, otherwise use the Array.prototype.forEach method, but if you want to have flow control on the iteration you want to use a traditional for loop.

Here are the pros and cons of each one of them:

for-of

  • Pros

    • Clean and easy to read.
    • Can use let and const ES6 bindings, this eliminates possible closure problems when working with functions within loops.
    • Can use flow control statements like break, continue and labels to potentially manage complex nested looping.
    • Can return from the enclosing function.
    • Can iterate over array like objects, like the arguments object, strings, Maps, other built-in iterables and custom iterators.
  • Cons

    • Requires you to be able to target ES6

Array.prototype.forEach

  • Pros

    • Clean and easy to read.
    • Provides the element index if needed without needing to keep track of incrementing a variable.
  • Cons

    • Cannot have complete control of its flow since we cannot use break, continue or labels.
    • Cannot return from the enclosing function.

for or any traditional sequential loop

  • Pros

    • Works on every platform.
    • Can be potentially the fastest way if performance is critical.
    • Can use flow control statements like break, continue and labels.
    • Can return from the enclosing function.
  • Cons

    • Some might consider it less "elegant" and harder to read.
    • Need to keep track and increment the index variable.

And remember, enumeration is not iteration!

Recommended read:

comment

Comments

arrow_back

Previous

Answering the Juriy Zaytsev JavaScript Quiz

Next

Answering the Juriy Zaytsev JavaScript Quiz
arrow_forward