开发者问题收集

为什么使用“for...in”进行数组迭代是一个坏主意?

2009-02-01
1050981

有人告诉我不要在 JavaScript 中将 for...in 与数组一起使用。为什么不行?

3个回答

原因是,一个构造:

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
*/
Kenan Banks
2009-02-01

for-in 语句本身并不是一个“坏习惯”,但它可能会被 误用 ,例如, 迭代 数组或类似数组的对象。

for-in 语句的目的是 枚举 对象属性。此语句将在原型链中向上移动,同时枚举 继承 的属性,而这 有时 是我们所不希望的。

此外,规范不保证迭代的顺序,这意味着,如果您想要“迭代”一个数组对象,使用此语句您不能确保属性(数组索引)将按数字顺序被访问。

例如,在 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
  }
}
Christian C. Salvadó
2010-11-23

有三个原因说明您不应该使用 for..in 来迭代数组元素:

  • for..in 将循环遍历数组对象的所有自有和继承属性,这些属性不是 DontEnum ;这意味着如果有人向特定数组对象添加属性(这样做有正当理由 - 我自己就这样做过)或更改 Array.prototype (这在应该与其他脚本配合良好的代码中被认为是不好的做法),这些属性也将被迭代;可以通过检查 hasOwnProperty() 来排除继承的属性,但这对数组对象本身中设置的属性没有帮助

  • for..in 不能保证保留元素顺序

  • 它很慢,因为您必须遍历数组对象的所有属性及其整个原型链,并且仍然只能获取属性的名称,即要获取值,需要进行额外的查找

Christoph
2009-02-01