I've been told not to use for...in
with arrays in JavaScript. Why not?
Why Using ‘for…in’ for Array Iteration in JavaScript Is a Bad Idea
arraysfor-loopiterationjavascriptloops
Related Solutions
First, the order of the loop is undefined for a for...in
loop, so there's no guarantee the properties will be iterated in the order you want.
Second, for...in
iterates over all enumerable properties of an object, including those inherited from its prototype. In the case of arrays, this could affect you if your code or any library included in your page has augmented the prototype of Array
, which can be a genuinely useful thing to do:
Array.prototype.remove = function(val) {
// Irrelevant implementation details
};
var a = ["a", "b", "c"];
for (var i in a) {
console.log(i);
}
// Logs 0, 1, 2, "remove" (though not necessarily in that order)
for..in (generally)
for..in
loops through the names of the properties of an object. People think it loops through the indexes of an array because the indexes of an array are names of properties on the array object, but that's a misconception.
So:
var obj = {foo: 1, bar: 2};
foo
and bar
are names of properties, and so:
var name;
for (name in obj) {
alert(name);
}
...will show "foo" and "bar" (in no particular order).
We'll come back to arrays in a moment. :-) Let's look at objects first.
Objects can have properties of their own, and properties they inherit from their prototype objects. The foo
and bar
properties above were direct properties of obj
, but:
function Thing() {
}
Thing.prototype.foo = 1;
var t = new Thing();
t.bar = 2;
Now our t
object has foo
and bar
, but foo
comes from the prototype, whereas bar
is its own property. In a for..in
, we can check which is which:
var name;
for (name in obj) {
alert(name + "(" + (obj.hasOwnProperty(name) ? "own" : "inherited") + ")");
}
...which will show "foo (inherited)" and "bar (own)" (in no particular order).
Objects can also have properties that are non-enumerable — they don't show up in for..in
loops. This is why if you do a for..in
on an array, the length
property doesn't show up, because length
is defined as a non-enumerable property. Nearly all of the standard properties on the standard objects are non-enumerable (including all of the ones that point to functions, like the toUpperCase
property on String
instances). (It used to be that only the ones in the spec could be non-enumerable, but ECMAScript 5th edition now provides ways for us to have our own non-enumerable properties.)
So, arrays. Arrays in Javascript are nothing like arrays in most other languages. (They are not contiguous blocks of memory, for one thing.) An array in Javascript is a standard object with a couple of special behaviors:
- The
length
property is always set to the next number above the property name with the highest numeric value that the array objects has. So if your highest array index is2
, thenlength
will be 3. - If you change
length
, any property whose name has a numeric value equal to or higher than the number you givelength
will be deleted from the object.
That, and the functions they inherit from the Array.prototype
are pretty much all that's special about arrays in Javascript. All of their main functionality (storing and retrieving values) is just the standard Javascript object property behavior.
Your specific loop
For the above reasons, I wouldn't use for..in
for your loop. I'd either use jQuery#each
or a boring old-fashioned loop like you have. The latter would look like this:
var allSelected = $("select option:selected");
var totalSelected = allSelected.length; // <= No need to repeat the selector query
var index;
var anySelected = false;
for (index = 0; !anySelected && index < totalSelected; ++index) {
if (allSelected[index].value !== "0") {
anySelected = true;
}
}
if (anySelected) {
// do something with the selection
}
else [
// do something about their not having picked one
}
Best Answer
The reason is that one construct:
can sometimes be totally different from the other:
Also consider that JavaScript libraries might do things like this, which will affect any array you create: