ige62g
Last Updated: February 25, 2016
·
949
· eneko89

Iteration in JavaScript done right

There are 4 iteration statements in JavaScript: for, for-in, while and do-while. Here are some tips about how and when to use them for maximum readability and performance, and also a few gotchas you should take into account.

For-in

If you are new to JavaScript, beware: unfortunately it's not the useful for-each construct you probably used in other languages (although for-of will be and it's in the way, but that's another topic).

JavaScript for-in iterates over an objects enumerable property keys, the own ones and those in it's prototype chain. You have probably read that you shouldn't use it with arrays, but not always why. Those are the gotchas with arrays and for-in loop:

  • For-in iterates over object property keys, and Arrays are just objects with integer keys and some useful methods. They could have other enumerable properties or inherit them from their prototype chain:
var arr = ['a', 'b', 'c'];
arr.foo = 'bar';

for (var key in arr) {
  // Logs 0-a, 1-b, 2-c, foo-bar.
  console.log(key + '-' + arr[key]);
}
  • For-int does not guarantee any iteration order, at least in a cross-browser setting.

  • For-in does not iterate over length. JavaScript arrays can be non-consecutive (or sparse), although they shouldn't. It's, in my opinion, a design flaw. Don't use arrays this way. If you need sparse indexes, use objects.

var arr = ['a', 'b', 'c'];
arr[100] = 'foo';

// It logs 101.
console.log(arr.length);

for (var key in arr) {
  // It logs 0, 1, 2 and 101.
  console.log(key);
}
  • Even if you are not worried about the above reasons, you should know that for-in is terribly slow. I would expect it to be two or three times slower, but it's worse: it seems that using it prevents compiler JIT optimizations, and you could easily end with something 100 times slower.

Does this mean that for-in is never useful? Not at all! Use it to iterate through an object's enumerable property keys. Filter with hasOwnProperty if you don't want properties in it's prototype chain.

var obj = { foo: 'bar', baz: 'qux' };

for (var key in obj) {
  if (obj.hasOwnProperty(key)) {
    // Logs foo, baz.
    console.log(key);
  }
}

In ES5, you also have Object.keys(obj), which returns an array with the object's own enumerable property keys.

var obj = { foo: 'bar', baz: 'qux' };
Object.keys(obj); // Returns ['foo', 'baz']

Another useful function is Object.getOwnPropertyNames(), which returns all of the objects own properties (non-enumerable ones also). With these, you shouldn't be using for-in too often.

For (and while)

The good ol' for loop is more intuitive than the for-in, which is good news for readability and ease of maintenance. It's quite fast also, even if you don't cache arr.length. Don't worry; those micro-optimizations are an easy job for today's JavaScript JIT compilers and don't improve performance by any means.

for (var i = 0; i < arr.length; i++) {
}

However, sometimes we could do it better. If you are iterating a temporary array and you don't mind removing element's, those are really stylish and readable:

var elem;

// Straight loop.
while(elem = arr.shift()) {
  // Code
}

// Backwards loop
while(elem = arr.pop()) {
  // Code
}

Aren't those great? They are even orders of magnitude faster than the good ol' for! But wait, there are also a few not-so-obvious gotchas with these. If the array contains any falsey value, for example a zero or an empty string, it will stop there and will not iterate from then on. We could fix it this way, as we know that shift() and pop() in an empty array return undefined:

// Backwards loop
while((elem=arr.pop()) !== undefined) {
  // Code
}

But it's no longer so readable, and it does not work for sparse arrays. No way.

This is what I found to be the most readable and performant way of looping through an array:

var arr = ['a', 'b', 'c'];

// If you don't mind removing elements
while(arr.length) {
  var elem = arr.pop(); // Or shift().
}

// If you don't want to remove element's
// and don't mind backwards looping.
var len = arr.length;
while(len--) {
  var elem = arr[len];
}

For more complex loops, use good ol' for, and if you have to iterate 1 to n instead of 0 to n, give do-while a chance.

There is also Array.forEach(), which is nice, but it's inherently slower and needs a function. Often, that entails creating a closure referencing variables you don't need, which is overkill.

Check this tests for performance comparisons between different loop types.