Why is using "for...in" for array iteration a bad idea?
I've been told not to use for...in
with arrays in JavaScript. Why not?
Solution 1:
The reason is that one construct:
var a = []; // Create a new empty array.
a[5] = 5; // Perfectly legal JavaScript that resizes the array.
for (var i = 0; i < a.length; i++) {
// Iterate over numeric indexes from 0 to 5, as everyone expects.
console.log(a[i]);
}
/* Will display:
undefined
undefined
undefined
undefined
undefined
5
*/
can sometimes be totally different from the other:
var a = [];
a[5] = 5;
for (var x in a) {
// Shows only the explicitly set index of "5", and ignores 0-4
console.log(x);
}
/* Will display:
5
*/
Also consider that JavaScript libraries might 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 (var x in a){
// Now foo is a part of EVERY array and
// will show up here as a value of 'x'.
console.log(x);
}
/* Will display:
0
1
2
3
4
foo
*/
Solution 2:
The for-in
statement by itself is not a "bad practice", however it can be mis-used, for example, to iterate over arrays or array-like objects.
The purpose of the for-in
statement is to enumerate over object properties. This statement will go up in the prototype chain, also enumerating over inherited properties, a thing that sometimes is not desired.
Also, the order of iteration is not guaranteed by the spec., meaning that if you want to "iterate" an array object, with this statement you cannot be sure that the properties (array indexes) will be visited in the numeric order.
For example, in JScript (IE <= 8), the order of enumeration even on Array objects is defined as the properties were created:
var array = [];
array[2] = 'c';
array[1] = 'b';
array[0] = 'a';
for (var p in array) {
//... p will be "2", "1" and "0" on IE
}
Also, speaking about inherited properties, if you, for example, extend the Array.prototype
object (like some libraries as MooTools do), that properties will be also enumerated:
Array.prototype.last = function () { return this[this.length-1]; };
for (var p in []) { // an empty array
// last will be enumerated
}
As I said before to iterate over arrays or array-like objects, the best thing is to use a sequential loop, such as a plain-old for
/while
loop.
When you want to enumerate only the own properties of an object (the ones that aren't inherited), you can use the hasOwnProperty
method:
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
// prop is not inherited
}
}
And some people even recommend calling the method directly from Object.prototype
to avoid having problems if somebody adds a property named hasOwnProperty
to our object:
for (var prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
// prop is not inherited
}
}