tags:

views:

4125

answers:

6

I've been told not to use "for...in" with Arrays in JavaScript but never received a satisfactory reason as to why not.

The question is: Why is using "for ...in" with array iteration such a bad idea?

+2  A: 

Because it enumerates through object fields, not indexes. You can get value with index "length" and I doubt you want this.

vava
So whats the best way of doing that?
lYriCAlsSH
for (var i = 0; i < arr.length; i++) {}
vava
In firefox 3 you could also use either arr.forEach or for(var [i, v] in Iterator(arr)) {} but neither of those works in IE, although you can write forEach method yourself.
vava
and virtually every library has it's own method for this too.
vava
This answer is wrong. "lenght" will not be included in for-in iteration. It is only properties you add yourself which is included.
JacquesB
+15  A: 

The reason is that one construct...

var a = [];
a[5] = 5; // Perfectly legal Javascript that resizes array

for (int i=0; i<a.length; i++) {
    // Iterates over numeric indexes from 0 to 4, as everyone expects
}

can sometimes be totally different from the other...

var a = [];
a[5] = 5;
for (x in a) {
    // Shows only the explicitly set index of "5", and ignores 0-4
}

Also consider that Javascript libraries often do things like this, which will affect any array you create:

// Somewhere deep in your javascript library...
Array.prototype.foo = 1;

// Now you have no idea what the below code will do.
var a = [1,2,3,4,5];
for (x in a){
    // Now foo is a part of EVERY array and 
    // will show up here as a value of 'x'
}
Triptych
Ouch. Sounds like a disaster waiting to happen. I didn't take into account the libraries doing something in the background to throw a wrench into the process. Thanks. again.
lYriCAlsSH
The second example is wrong. length is DontEnum, which means it will not be iterated over. It is only when you add you own properties you get a problem.
JacquesB
thanks olavk, I changed to another example.
Triptych
Historically, some browsers even iterated over 'length', 'toString' etc.!
bobince
+5  A: 

Because for...in enumerates through the object that holds the array, not the array itself. If I add a function to the arrays prototype chain, that will also be included. I.e.

Array.prototype.myOwnFunction = function() { alert(this); }
a = new Array();
a[0] = 'foo';
a[1] = 'bar';
for(x in a){
 document.write(x + ' = ' + a[x]);
}

This will write:

0 = foo
1 = bar
myOwnFunction = function() { alert(this; }

And since you can never be sure that nothing will be added to the prototype chain just use a for loop to enumerate the array:

for(i=0,x=a.length;i<x;i++){
 document.write(x + ' = ' + a[x]);
}

This will write:

0 = foo
1 = bar
Pim Jager
+7  A: 

In isolation, there is nothing wrong with using for-in on arrays. For-in iterates over the property names of an object, and in the case of an "out-of-the-box" array, the properties corresponds to the array indexes. (The built-in propertes like length, toString and so on are not included in the iteration.)

However, if your code (or the framework you are using) add custom properties to arrays or to the array prototype, then these properties will be included in the iteration, which is probably not what you want.

Some JS frameworks, like Prototype modifies the Array prototype. Other frameworks like JQuery doesn't, so with JQuery you can safely use for-in.

If you are in doubt, you probably shouldn't use for-in.

An alternative way of iterating through an array is using a for-loop:

for (var ix=0;i<arr.length;ix++) alert(ix);

However, this have a different issue. The issue is that a JavaScript array can have "holes". If you define arr as:

var arr = ["hello"];
arr[100] = "goodbuy";

Then the array have two items, but a length of 100. Using for-in will yield two indexes, while the for-loop will yield 101 indexes, where the 99 has a value of undefined.

JacquesB
+3  A: 

There are three reasons why you shouldn't use for..in to iterate over array elements:

  • for..in will loop over all own and inherited properties of the array object which aren't DontEnum; that means if someone adds properties to the specific array object (there are valid reasons for this - I've done so myself) or changed Array.prototype (which is considered bad practice in code which is supposed to work well with other scripts), these properties will be iterated over as well; inherited properties can be excluded by checking hasOwnProperty(), but that won't help you with properties set in the array object itself

  • for..in isn't guaranteed to preserve element ordering

  • it's slow because you have to walk all properties of the array object and its whole prototype chain and will still only get the property's name, ie to get the value, an additional lookup will be required

Christoph
+2  A: 

In addition to the reasons given in other answers, you may not want to use the "for...in" structure if you need to do math with the counter variable because the loop iterates through the names of the object's properties and so the variable is a string.

For example,

for (var i=0; i<a.length; i++) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

will write

0, number, 1
1, number, 2
...

whereas,

for (var ii in a) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

will write

0, string, 01
1, string, 11
...

Of course, this can easily be overcome by including

ii = parseInt(ii);

in the loop, but the first structure is more direct.

ctmiddle