In for loops you iterate over arrays or array-like objects such as
arguments and HTMLCollection objects. The
usual for loop pattern looks like the
following:
// sub-optimal loop
for (var i = 0; i < myarray.length; i++) {
// do something with myarray[i]
}
A problem with this pattern is that the length of the array is
accessed on every loop iteration. This can slow down your code,
especially when myarray is not an
array but an HTMLCollection
object.
HTMLCollections are objects
returned by DOM methods such as:
document.getElementsByName()
document.getElementsByClassName()
document.getElementsByTagName()
There are also a number of other HTMLCollections, which were introduced before
the DOM standard and are still in use today. There include (among
others):
document.images
-
All IMG elements on the page
document.links
-
All A elements
document.forms
-
All forms
document.forms[0].elements
-
All fields in the first form on the page
The trouble with collections is that they are live queries against
the underlying document (the HTML page). This means that every time you
access any collection’s length, you’re
querying the live DOM, and DOM operations are expensive in
general.
That’s why a better pattern for for loops is to cache the length of the array
(or collection) you’re iterating over, as shown in the following
example:
for (var i = 0, max = myarray.length; i < max; i++) {
// do something with myarray[i]
}
This way you retrieve the value of length only once and use it during the whole
loop.
Caching the length when iterating over HTMLCollections is
faster across all browsers—anywhere between two times faster (Safari 3)
and 190 times (IE7). (For more details, see High
Performance JavaScript by Nicholas Zakas [O’Reilly].)
Note that when you explicitly intend to modify the collection in
the loop (for example, by adding more DOM elements), you’d probably like
the length to be updated and not
constant.
Following the single var
pattern, you can also take the var out of the loop and make the loop
like:
function looper() {
var i = 0,
max,
myarray = [];
// ...
for (i = 0, max = myarray.length; i < max; i++) {
// do something with myarray[i]
}
}
This pattern has the benefit of consistency because you stick to
the single var pattern. A drawback is
that it makes it a little harder to copy and paste whole loops while
refactoring code. For example, if you’re copying the loop from one
function to another, you have to make sure you also carry over i and max
into the new function (and probably delete them from the original
function if they are no longer needed there).
One last tweak to the loop would be to substitute i++ with either one of these expressions:
i = i + 1
i += 1
JSLint prompts you to do it; the reason being that ++ and --
promote “excessive trickiness.” If you disagree with this, you can set
the JSLint option plusplus to
false. (It’s true by default.) The last
pattern is used: i += 1.
Two variations of the for
pattern introduce some micro-optimizations because they:
Use one less variable (no max)
Count down to 0, which is usually faster because it’s more
efficient to compare to 0 than to the length of the array or to
anything other than 0
The first modified pattern is:
var i, myarray = [];
for (i = myarray.length; i--;) {
// do something with myarray[i]
}
And the second uses a while
loop:
var myarray = [],
i = myarray.length;
while (i--) {
// do something with myarray[i]
}
These are micro-optimizations and will only be noticed in
performance-critical operations. Additionally, JSLint will complain
about the use of i--.