Tip - Looping Through Arrays

Don't use for…in:

var a, index;
a = ['a', 'b', 'c'];
for (index in a)  // <= WRONG
{
    alert(index + ' = ' + a[index]);
}

Despite what you may have been told, for..in is not for iterating through the indexes of an array. for..in iterates through the property names of an object. Arrays are objects, and their element indexes are property names (array elements are really just object properties), but arrays can have other properties as well, which will break your for..in loops if you assume they only loop through array indexes. For example:
var a, index;
a = ['a', 'b', 'c'];
a.name = 'MyArray';
for (index in a)  // <= WRONG
{
    alert(index + ' = ' + a[index]);
}

That shows the elements, and also "name = MyArray". The name property isn't an element, but it shows up in the loop, because the loop is going through the properties of the object. ("But wait," you're saying, "if for..in loops through the property names, why don't I see the 'length' property?" The answer is that some properties aren't enumerable and so they're exempt from for..in loops. This is part of the ECMAScript spec. You can't set your own properties to be non-enumerable, but many of the intrinsic properties of various objects are marked that way per the spec.)

Prototype adds a bunch of properties to array objects (mostly functions — remember that functions can be properties of objects just like anything else), and this has lead to people saying "Prototype breaks my loops". Respectfully, the loops were already broken ;-) — it's just that they seemed to mostly work in a specific limited environment.

Instead, use boring old-fashioned stuff like this:

var a, index;
a = ['a', 'b', 'c'];
for (index = 0; index < a.length; ++index)
{
    alert(index + ' = ' + a[index]);
}

…or cache the length (faster with long arrays):
var a, index, len;
a = ['a', 'b', 'c'];
for (len = a.length, index = 0; index < len; ++index)
{
    alert(index + ' = ' + a[index]);
}

…or go backward when you don't care what order you loop in (faster in some — not all — environments with really long arrays):
var a, index;
a = ['a', 'b', 'c'];
for (index = a.length - 1; index >= 0; --index)
{
    alert(index + ' = ' + a[index]);
}

…or use the nifty Enumerable.each method, which is mixed into Arrays by Prototype:
var a;
a = ['a', 'b', 'c'];
a.each(function(value, index){
    alert(index + ' = ' + value);
});

Enumberable.each is really powerful, and really handy, but note that it does mean the code executes a function call (actually, behind the scenes, a couple) on every iteration. Now, if there's a lot of logic in the body of the loop, breaking it out into a separate function (probably a named function rather than the anonymous one we used above) is best practice anyway. Even if the loop body is small, 9 times out of 10 the added overhead of using each doesn't matter and you benefit from the clarity and ease of maintenance. But for highly-optimized loops where execution speed is a critical factor, stick to the boring old-fashioned stuff.

Enumerable.invoke is another handy feature: If you call it on an array of objects, it invokes the method name you give on each of the objects:

var elements;
elements = $$('div.detail');
elements.invoke('hide');

That uses $$() to find all of the divs with class detail in the document and calls their hide() method. Handy if the objects already have the method.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License