javascript - Why is using "for...in" for array iteration a bad idea?

ID : 459

viewed : 131

Tags : javascriptarraysloopsfor-loopiterationjavascript





Top 5 Answer for javascript - Why is using "for...in" for array iteration a bad idea?

vote vote

99

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  */

vote vote

81

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   } } 
vote vote

70

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

vote vote

68

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(i + ' = ' + a[i]); } 

This will write:

 0 = foo 1 = bar 
vote vote

59

As of 2016 (ES6) we may use for…of for array iteration, as John Slegers already noticed.

I would just like to add this simple demonstration code, to make things clearer:

Array.prototype.foo = 1; var arr = []; arr[5] = "xyz";  console.log("for...of:"); var count = 0; for (var item of arr) {     console.log(count + ":", item);     count++;     }  console.log("for...in:"); count = 0; for (var item in arr) {     console.log(count + ":", item);     count++;     } 

The console shows:

for...of:  0: undefined 1: undefined 2: undefined 3: undefined 4: undefined 5: xyz  for...in:  0: 5 1: foo 

In other words:

  • for...of counts from 0 to 5, and also ignores Array.prototype.foo. It shows array values.

  • for...in lists only the 5, ignoring undefined array indexes, but adding foo. It shows array property names.

Top 3 video Explaining javascript - Why is using "for...in" for array iteration a bad idea?







Related QUESTION?