为什么在JavaScript中,使用'for(var item in list)'遍历数组被认为是不良实践?

85

给定一个简单的从零开始、数值索引的数组:

var list = ['Foo', 'Bar', 'Baz'];

很多时候,我注意到当有人建议像这样循环遍历数组中的变量:

for(var item in list) { ... }

几乎肯定有人会建议这样做是不好的实践,并提出替代方法:

var count = list.length;

for(var i = 0; i < count; i++) {
    var item = list[i];
    ...
}

为什么不使用上面更简单的版本,而要使用第二个例子呢?


你并没有使用循环遍历项目,而是在遍历键/属性名称/索引。 - Bergi
5
维多利亚高礼帽风格的C语言编程者也不理解迭代器。但请记住它输出的是键而不是值。for( ; ; )格式更快,但99%的情况下,这并不重要。程序员的时间比计算时间更昂贵,除非你在处理大型项目或需要真正进行优化的东西。 - Shayne
5个回答

101

首先,对于 for...in 循环,循环顺序是未定义的,因此不能保证属性按照您期望的顺序迭代。

其次,for...in 循环会遍历对象的所有可枚举属性,包括从原型继承的属性。对于数组来说,如果您的代码或页面中包含的任何库已经增强了 Array 的原型,则可能会受到影响,这可能是一个真正有用的操作:

Array.prototype.remove = function(val) {
    // Irrelevant implementation details
};

var a = ["a", "b", "c"];

for (var i in a) {
    console.log(i);
}

// Logs 0, 1, 2, "remove" (though not necessarily in that order)

2
只要使用hasOwnProperty,那就没问题了 - for (var i in a) { if (a.hasOwnProperty(i)) console.log(i); } -> 1 2 3 - Dimitar Christoff
11
建议将“over all properties”更改为“over enumerable properties”。在较新的JavaScript实现中,属性可以定义为具有“enumerable”属性设置为“false”的情况。这些属性以及内置JavaScript对象的属性不会显示在“for...in”循环中。 - Andy E
4
@Dimitar: 的确,尽管一旦你添加了这个,循环就不再比标准的C风格for循环更简单了。 - Tim Down
1
无论如何 - 像那样循环数组的唯一合法理由是获取数组键(这归功于JavaScript中数组的本质)- 任何其他理由,最好使用普通循环。 - Dimitar Christoff

20

5
我误读了那句话,以为无限循环比普通循环快36倍。感谢提供链接 +1。 - Thomas O
@Thomas 合理的误解 - 当脱离上下文阅读时。已修复 :) - Amarghosh

2

for ... in ...不能返回列表项,而是枚举数组属性。

仅因此原因,它无法替代for(i=0; i<arr.length; i++)循环。

适当的替代方案是for ... of ...结构。它枚举可迭代对象(例如数组)的值。您可以在MDN Web Docs上阅读更多信息:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of

它受到相关现代浏览器的支持(Internet Explorer不算,因为它已被Microsoft Edge取代)。如果您可以承受不支持旧版浏览器,那么这可能是正确的方法。您可以检查前面链接的MDN页面末尾的方便的浏览器支持表,以查看哪些浏览器版本实际允许使用for ... of ...


我很高兴能知道你对IE的断言是真实可信的 :) - CapelliC

1
如果您像这样使用for/in,item将枚举字符串值“0”,“1”等,而不是列表中的实际对象。因此,第一个片段中的“item”更像第二个片段中的i,而不是item。此外,字符串值在您期望数字的位置被枚举。当您将属性添加到列表中时,例如array.ID = "a123",它们也会被枚举,从而导致问题。
但是,尽管存在这些缺点,我仍然认为语法非常有用,如果您的团队知道它的作用。

0
添加 list.foo = bar; 并尝试使用简单的 for 循环。 如果您不使用某些库(如 prototypeJs)并且不向数组对象添加任何新属性,则可以使用简单的 for 语句。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接