为什么使用“for...in“对于数组迭代来说是个坏主意?

2022-08-29 21:55:40

我被告知不要在JavaScript中使用数组。为什么不呢?for...in


答案 1

原因是一个构造:

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

答案 2

该语句本身并不是一种“不好的做法”,但是它可能被误用,例如,循环访问数组或类似数组的对象。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
}

另外,谈到继承的属性,例如,如果您扩展对象(例如MooTools所做的某些库),则该属性也将被枚举:Array.prototype

Array.prototype.last = function () { return this[this.length-1]; };

for (var p in []) { // an empty array
  // last will be enumerated
}

正如我之前所说,迭代数组或类似数组的对象,最好的办法是使用顺序循环,例如普通的/循环。forwhile

如果只想枚举对象的自身属性(未继承的属性),可以使用以下方法:hasOwnProperty

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

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

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