Evaluating JavaScript Iteration Techniques

It has been a while since we looked at the performance of various iteration techniques in JavaScript. Since most developers use a JavaScript framework nowadays, you may not even be aware of the trade-offs being made each time you call $.each or your library’s equivalent iteration function. This article describes the four most common iteration techniques and how well they perform on several browsers, and compares it to the native browser Array.prototype.forEach.

Article updated on Jan. 19, 2013

Added native Array.prototype.forEach analysis.

How do it…

Version 1 - A simple for loop (iterate and access the item from an array):

for (var i= 0; i < 100000; i += 1) {
	aData[i] = i;
}

Version 2 - A simple each function that passes the value to the iteration callback function:

function _each(arr, fn) {
	for (var i = 0, j = arr.length; i < j; i +=1 ) {
		fn(arr[i]);
	}
}

Version 3 - An each function that passes the value to iteration callback function and changes the execution context using call:

function _each_call(arr, fn, ctx) {
	if (! ctx) {
		ctx = this;
	}

	for (var i = 0, j = arr.length; i < j; i +=1 ) {
		fn.call(ctx, arr[i]);
	}
}

Version 4 - An each function that passes value to the iteration callback function, changes execution context, and adds additional arguments using apply:

function _each_apply(arr, fn, ctx, args) {
	if (! ctx) {
		ctx = this;
	}

	if (! args) {
		args = [];
	}

	args.unshift(undefined);

	for (var i = 0, j = arr.length; i < j; i +=1 ) {
		args[0] = arr[i];
		fn.apply(ctx, args);
	}
}

Native 1 - The native forEach without changing execution context:

arr.forEach(function(item) {

});

Native 2 - The native forEach while changing execution context:

arr.forEach(function(item) {

}, this);

Run the test on your browser (will print to the console):

Test iteration

How it works…

Most JavaScript libraries use some variation/combination of the above functions for their built in iteration function. On my machine, I calculated the following average values for each of the above methods by iterating over a 100,000 item array:

MethodChrome v22FireFox v15Safari v6
for loop0.2ms0.6ms7ms
each function0.7ms22.0ms43.5ms
each function w/ call1.1ms31.8ms44.7ms
each function w/ apply5.1ms34.1ms50.0ms
native w/o context change4.1ms27.5ms37.9ms
native w/ context change4.0ms27.5ms37.9ms

As you might expect, the basic for loop operation is the most performant and Chrome is the fastest for all operations. The difference between a basic for loop and the each method, is only a function call on each item in the array, which marginally affects performance in Chrome, but noticeable affects other browsers. Using the call operation to call the iteration callback function and change its execution context is marginally slower in Chrome and Safari, but noticeably slower in FireFox. Lastly, using apply to modify the execution context and change the arguments passed into the iteration callback function is noticeable slower in Chrome and Safari, but only marginally slower in FireFox. Besides setting the arguments, the apply technique also has to update the first element in the provided arguments array, which marginally affects performance in addition to the cost of applying the arguments and changing execution context.

Comparing the various iteration techniques to the native Array.prototype.forEach method, we find that in Chrome and FireFox the basic iteration function outperforms the native, but there is a small performance gain in Safari. I expected the native method to outperform using the call technique in all browsers as well, but in Chrome we still see faster performance using a custom iteration function and call to change the execution context; other browsers are about the same with or without changing the execution context. Oddly, whatever jutsu Chrome is using to implement the native forEach is ever so slightly faster when changing the execution context.

There’s more…

I am surprised that there is so much difference between the browsers, since they have all been working on improving performance. That said, none of the browsers performed slowly, and none of the techniques feel much slower to the end user. It takes iterating over a list of 100,000 items just to get enough signal (in milliseconds) for a meaningful analysis, and few JavaScript-driven web applications deal with data sets of that scale. If you can use the native iteration function instead, then you can get away with changing the execution context without any performance hit, so there is less code complexity. However, if you are optimizing the native function is noticeably slower in Chrome, so you might choose not to use for that one browser.

Whatever operation you are doing inside the for loop or iteration callback function will affect performance much more than the iteration technique, so don’ worry about using your library’s iteration function. First focus your efforts on speeding up the iteration callback function, then the iteration technique itself.

However, if you are working with large data sets, or frequently iterate over smaller data sets, you may experience a noticeable improvement by paying attention to the iteration technique. Avoid using an iteration function that uses call or apply when you do not care about the execution context of your iteration callback function and don’t need to modified the arguments. You may even consider using a basic for loop, to get the best performance out of all browsers.