为什么在数组迭代中用“ for... in”不太好?

有人告诉我不要在 JavaScript 的数组中使用 for...in。为啥?

原因在于一个结构:

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

有时候是完全不同的

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

还要考虑到 JavaScript 库可能会这样,这会影响你创建的数组:

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

for-in语句本身没错,但是它可能会用错,例如,在数组或类似数组的对象上进行迭代。
For-in 语句的用途是枚举多个对象属性。该语句将在原型链中上行,同时枚举了多个继承的属性,但有时不需要的。
此外,迭代的顺序不受 spec. 的保证,这意味着如果您想“迭代”一个数组对象,使用这个语句,您不能确保属性(数组索引)将以数字顺序访问。
例如,在 JScript (IE 8)中,甚至在 Array 对象上的枚举次序被定义为:

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}

另外,说到继承的属性,例如,扩展 Array.prototype 对象(像 MooTools 这样的一些库一样) ,这些属性也会被枚举:

Array.prototype.last = function () { return this[this.length-1]; };
for (var p in []) { // an empty array
  // last will be enumerated}

正如我前面所说的,对数组或类似数组的对象进行迭代时,最好使用顺序循环,比如普通的 for/while 循环。
当你只想枚举一个对象自己的属性(那些没有被继承的) ,你可以使用 hasOwnProperty方法:

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    // prop is not inherited
  }}

有些人甚至建议直接从 Object.prototype 调用该方法,以避免在有人向我们的对象添加名为 hasOwnProperty 的属性时出现问题:

for (var prop in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    // prop is not inherited
  }}

因为 for... in 枚举通过保存数组的对象,而不是数组本身。如果我向数组原型链添加一个函数,它也会包含在内。 即

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

这样写:

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

因为你永远不能确定原型链中不会添加任何东西,所以只需使用 for 循环来枚举数组:

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

这样写

0 = foo
1 = bar