为啥不建议数组使用 JavaScript 的 For...In 循环? [复制]
Posted
技术标签:
【中文标题】为啥不建议数组使用 JavaScript 的 For...In 循环? [复制]【英文标题】:Why is JavaScript's For...In loop not recommended for arrays? [duplicate]为什么不建议数组使用 JavaScript 的 For...In 循环? [复制] 【发布时间】:2011-07-13 06:56:09 【问题描述】:我在某处读到(抱歉,我找不到链接),不建议将 For...In 循环用于数组。这里说:http://www.openjs.com/articles/for_loop.php 用于关联数组,http://www.w3schools.com/js/js_loop_for_in.asp 用于迭代对象的所有属性(它并没有说它可以用于数组)。我不知道该相信谁。我不希望这个问题成为辩论。我只想知道我是否可以在我的代码中使用它而不会产生不可预见的副作用。谢谢!
【问题讨论】:
W3 学校? W3Fools!尽管已经说过任何声称 javascript 具有“关联数组”的东西也是错误的 @Gareth Nice 链接,但是,JavaScript 确实 有 "associative arrays"。 Perl 甚至这样称呼它们,尽管“字典”或“地图”是其他地方的常用术语。 (我更喜欢避免使用“关联数组”一词,因为我喜欢与“普通数组”ADT 保持区别;但这并不会使该术语不正确)。 @pst 很多人将 javascript 对象视为关联数组,但它们并不完全相同。这是迂腐的,但不同之处在于所有对象属性都是字符串。例如,array = ; array[42] = 'foo'; array["42"] // => 'foo' (!)
。对于数字,这不是那疯狂,但对于对象(我希望能够用作“关联数组”中的键)它只是不起作用:var key1 = name: 'Gareth', key2 = , array = ; array[key1] = 'awesome'; array[key2] // => 'awesome' (!)
在这个情况下,两个“键”具有相同的.toString()
,因此设置相同的属性
@pst 请注意,链接的 Wikipedia 页面第一部分的要求之一是“关联数组的键也可以任意键入”
【参考方案1】:
数组是一个对象,数组元素只是将数字索引转换为字符串的属性。例如,arr[123] 引用数组对象 arr 中的一个属性“123”。
for ... in
构造适用于所有对象,而不仅仅是数组,这会引起混淆。
当某人for ... in
一个数组时,程序员打算经常只迭代所有元素,甚至很可能按顺序。例如,如果数组包含一堆数字,那么程序员很可能打算迭代一个数字流。语义与其他编程语言中的数组迭代非常相似,很容易混淆。
在 JavaScript 中,此构造不会按顺序迭代数组元素。它迭代所有数组的属性名称(包括继承的原型函数的名称、添加到其中的任何属性、添加到其中的任何其他非元素属性等),并且按顺序排列全部。在早期的浏览器中,它甚至会找到属性length
,尽管在最近的浏览器中,这些属性现在被定义为隐藏正是出于这个确切的原因——人们不断地绊倒它!
使用上面的整数数组,您得到的不是数字流,而是文本字符串流。而不是元素值,而是属性的名称(它们只是数字索引,没有任何顺序)。这很可能不是程序员的意思,如果他/她来自另一种编程语言。如果存储在数组中的元素碰巧是相似的数值,那会让每个人都感到困惑。
这就是为什么你不应该这样做。您不应该使用一种看起来像是在做显而易见的事情,但实际上却在做完全不同的事情的语言结构。它会产生非常隐蔽且很难找到的错误。
【讨论】:
+1,尤其是“按顺序”部分 老实说,这个答案比标记为重复原始的那个更清楚原因,哈哈【参考方案2】:使用for(... in ...)
遍历数组不会得到数字键,它会得到字符串值。
它将为您提供在原型上定义的属性,因此如果有任何代码扩展 Array
,例如通过这样做:
Array.prototype.each = ...;
然后您将看到属性each
。
不保证您将按数组索引顺序获取属性。例如。尝试迭代
var arr = []
arr[1] = 1;
arr[0] = 0;
在许多浏览器上,您会在0
之前获得1
。
并且不能保证您获得所有索引。尝试迭代
[0,,,3]
你不会得到索引1
或2
。
【讨论】:
【参考方案3】:Prototype.js 库文档中给出了几个很好的理由: http://www.prototypejs.org/api/array
基本上,使用 for...in 来迭代数组是很脆弱的,因为任何其他代码都可以向 Array 原型添加属性,然后这些属性将成为每个数组对象的可枚举属性。
【讨论】:
【参考方案4】:我已经在几种浏览器(FireFox 3、Opera 9、IE6、IE9 beta、Chrome)中测试了数组迭代,并且效果很好;我似乎记得有些跨浏览器不兼容,但我一定是弄错了。
但仍有一个警告:
正如您所提到的,for ... in
语法用于迭代对象的属性;因此,对于数组,该数组对象的所有属性也将与元素一起迭代。通常,数组对象只有与其中的键对应的属性,但如果另一个脚本修改了Array.prototype
(例如某些框架),那么添加的方法/属性也会意外地出现在迭代中。
【讨论】:
你能举出这些浏览器的例子吗? @Jairo:我不记得具体是哪些,但我清楚地记得必须修复我的所有代码,因为我希望它是跨浏览器兼容的,而且它不能在我的浏览器上运行正在测试它(可能是 IE6?) IE6 给出:P。嗯。感谢您澄清您的回答。 @Jairo:我刚刚在一堆浏览器(包括 IE6)上使用for ... in
测试了一个示例,它工作正常,我必须出于另一个原因更改我的 JS(可能是原型之一)以上是关于为啥不建议数组使用 JavaScript 的 For...In 循环? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
为啥我们不应该在 javascript 中使用 ++? [复制]
为啥 Intellij Idea 建议在使用循环将数组转换为 Set 时创建中间列表?