The only for loop you will ever need *
* main contain hyperbole to take the mickey out of other blog posts
I am a very lazy person when it comes to typing. As I am doing so much of it I’d rather not type something I really don’t need. This is why I had an issue with “for” loops as you need to type them so often.
The classic way of looping through an array for example is a for loop with an iterator:
var arr = [1,2,3,4,5];
for( var i=0; i<arr.length; i++ ){
alert( arr[i] );
}
This is however not a good plan for large arrays (like DOM trees) as the length attribute is read out every time the loop executes. You can avoid this in two ways. If it is for example irrelevant in which order you loop through the array you can use a reverse loop:
var arr = [1,2,3,4,5];
for( var i=arr.length - 1; i> -1; i -- ) {
alert( arr[i] );
}
If you really need to go through the array in the normal order then you need to use another variable to store the length and compare with that one.
var arr = [1,2,3,4,5];
for( var i=0,j=arr.length; i<j ; i++ ){
alert( arr[i] );
}
The for loop also allows you to use the in keyword to make it easier:
for(i in arr ){
alert(arr[i]);
}
However, there is also another trick if you don’t want to do this (for example if you don’t want to iterate through all but use a step of 2). The comparison inside the loop does not need to be the iterator but could be any comparison. As long as it is true, the loop gets executed, when it becomes false the loop ends. This allows you to simply check for the array element inside the loop itself which means you don’t need to read the length attribute at all:
var arr = [1,2,3,4,5];
for(var i=0; arr[i]; i++ ){
alert( arr[i] );
}
There is however the issue of the truthy and falsy and type casting in JavaScript which means that if your array has null or 0 elements the loop would end, too:
var arr = [1,2,0,4,5];
for(var i=0; arr[i]; i++ ){
alert( arr[i] );
}
You can avoid this by testing properly:
var arr = [1,2,0,4,5];
for(var i=0; arr[i]!==undefined; i++ ){
alert( arr[i] );
}
There is also another problem this kind of loop avoids: if you manipulate the array during execution of the loop (for example by removing or moving DOM nodes) you need to change the iterator, too. This example will be an endless loop and lock your browser:
var u = document.getElementsByTagName('ul')[0];
var lis = u.getElementsByTagName('li');
for( var i=0; i<lis.length; i++ ){
u.removeChild(lis[i]);
}
With the other kind of loop that is not a problem. You can for example remove all list items from a UL with:
var u = document.getElementsByTagName('ul')[0];
var lis = u.getElementsByTagName('li');
for( var i=0; lis[0]; i++ ){
u.removeChild( lis[0] );
}
Did I miss something obviously bad about this idea? If so, please tell me, but also consider the other benefits of this for loop to end all for loops:
- a lot smaller than the YUI, Dojo or Prototype
- unobtrusive and accessible (if the array is not there nothing happens)
- may have rounded corners (but they are invisible)
- cross-browser
- easy to read
- heals sick animals when you don’t look
If you wonder about the speed implications of the different styles, check out the speed comparison of for loops test page.
Tags: comparison, for, javascript, loops, optimization, webdevtrick


April 5th, 2007 at 12:27 am
April 13th, 2007 at 5:50 pm
April 19th, 2007 at 8:52 pm
April 3rd, 2007 at 7:17 am
An interesting read. I like these back to basics type posts.
April 3rd, 2007 at 3:02 pm
This was most helpful, for one thing it’s nice to see someone explain why a reverse loop is faster (I knew it was but not why) and for another, it’s nice to know how to deal with changing sets of data that you’re looping over (i.e. removing node) … I’ve had this problem before but have only been able to deal with it with copies of arrays in the past (iterate over the copy while modifying the original)… great writeup!
April 3rd, 2007 at 6:01 pm
I’ve been using the (i=0;arr[i];i++) style for a few years now, ever since I saw it posted on the wdf-dom list. never once thought about 0 or null array values could break it, so thanks for that!
April 3rd, 2007 at 7:36 pm
In your final example, I can think of a better way to remove children from a DOM tree.
var u = document.getElementsByTagName(‘ul’)0;
while (u.childNodes0) { u.removeChild(u.lastChild) };
I know it’s not a “for”…
The lastChild only forces recalculation on .length and .lastChild in the DOM, whereas removing the firstChild of a tree means the DOM needs to recalculate several properties (.firstChild, any CSS rules using :first-child, :first-line, :first-letter, etc) in additional to re-indexing the DOM query.
April 3rd, 2007 at 7:37 pm
var arr = [1,2,0,4,5];
var i = arr.length; while (i--) {
alert(arr[i]);
}
Your reverse for-loop-fu is strong, but no match for superior reverse while-loop.
April 3rd, 2007 at 10:16 pm
for(i in arr ){
alert(arr[i]);
}
Regarding this: The for…in syntax isn’t meant to be used for arrays, but for getting the methods and properties of an object. If you use that syntax on an array that has been extended (as is the case when you use prototype) it’ll return the added properties and methods as well.
April 4th, 2007 at 7:34 am
@Ara Pehlivanian:
If I’m not mistaking, reverse loops are faster, because it’s easier for a computer to check if a value is 0. It doesn’t even have to look at the value: if there’s something there, it ain’t 0, so the comparison fails.
April 4th, 2007 at 8:57 pm
@Victor Welling
Is that actually true? I remember that one of the first lessons I was taught about coding was that null is nothing (i.e. there ain’t anything there), but that 0 is a value (i.e. there is something there)
April 5th, 2007 at 7:26 am
Fine explanation…I came across the reverse looping in Pro JS Techniques (apress)…and wondered why the John had written it that way.
Now I know :)
ps – i’m not exactly sure how to interpret your speed test results.
April 16th, 2007 at 7:30 pm
@Barry
Yes, null is nothing (as in, not even a single bit). 0 is a single, yet false (or off) bit. Which, as far as I know, is also rather easy to check for a computer. But, like I said, I might be mistaking ;)
May 10th, 2007 at 10:18 am
Ran into a problem today: when using the “array[i] !== undefined” evaluation in Safari to loop through a nodelist (returned by getElementsByTagName), the evaluation never returned false, even at the end of the array. This is due to the fact that nonexistent entries are not “undefined”, but “null” in Safari.
Rather annoying.
May 10th, 2007 at 11:02 am
@Victor – Yes, I had to rewrite a script because of this issue too.
It’s fixed in the latest webkit nightly
October 28th, 2007 at 1:28 am
Hey, that was a fun read.
Going all-out, you might also do:
for(var i=-1,e; (e=myarray[++i])!==undefined;)
{
// variable e already holds the value from your array
}
.. since assignment also evaluates to its result.
At this point however you have to ask yourself if it’s worth the readability. These tests show differences in fairly pretty degenerate cases as they go through a shitload of iterations without doing anything – it’s much more likely the inside of the loop will matter far far more.
Of course everyone should be discouraged from ever iterating an array with a for-in loop.