JavaScript技巧之不要用for in语句对数组进行遍历
本文详细解析了JavaScript中使用 for...in 遍历数组的潜在风险。当原生 Array 类被第三方库(如 prototype.js)通过原型链扩展时,for...in 会意外迭代这些新添加的方法属性,而非仅遍历数值索引,从而导致如 jqModal 插件在特定浏览器下失效等严重Bug。文章提供了避免该问题的正确操作方案。
一、为什么要避免使用 for...in 遍历数组
在前端开发中,许多开发者曾被告知不要使用 for...in 语句对数组进行遍历,除了性能因素外,更主要的原因是可能产生意料之外的 Bug。以 jQuery 插件 jqModal 为例,其源码内部存在类似如下嵌套循环:
for(var i in {jqmShow:1,jqmHide:1})
for(var s in this[i])
if(H[this[i][s]])
H[this[i][s]].w[i](this);
return F;
}
在此代码中,第一个 for...in 遍历的是匿名对象,没有问题。但第二个 for...in 遍历的目标 this[i] 根据上下文确认为一个数组对象(Array)。这种写法在处理标准原生数组时看似正常,但在特定环境下极易引发故障。
二、问题重现与原理分析
第1步:观察原生数组的行为
在标准的 JavaScript 环境中,如果 Array 未被任何修改,以下代码逻辑符合预期:
var x=[1];
for(var s in x){
alert(s);
};
按常理,上面语句应只执行一次 alert 方法,且变量 s 的值为字符串 "0"(数组索引)。
第2步:检查原型扩展后的差异
for...in 语句的潜在 bug 在于:它会枚举对象自身及其原型链上所有可枚举的属性。如果原生 Array 类被其他 JavaScript 库进行了原型扩展(例如添加了 toJSON 方法),即 Array.prototype.toJSON=xxxx,那么用 for...in 遍历扩展后的 Array 对象的逻辑将与遍历原生 Array 的逻辑发生显著差异。
举例来说,在上述代码运行时,如果环境中的 Array 已被扩展,上面的语句将执行两次 alert:
1. 第一次 s 为索引 "0";
2. 第二次 s 为方法名 "toJSON"。
第3步:冲突导致的实际故障
这种扩展行为在很多知名库中普遍存在,最著名的例子是 prototype.js,它为 Array 类扩展了诸如 toJSON、each 等众多方法。
如果设计的代码逻辑以原生 Array 为基准,某天页面中引入了此类库,原有代码逻辑将不再成立。
具体案例:
如果你在使用 jqModal 的同时,因其他原因也引用了 prototype.js,就会遇到严重的兼容性问题。冲突将导致 jqModal 的弹框在 IE6、IE7 下面无法利用 closeClass 设置的按钮进行自动关闭。跟踪调试会发现,异常之处正是 jqModal 源码中那个不当的 for...in 循环。
三、解决方案
要避免此类问题,核心原则是不依赖 for...in 来遍历数组元素。在需要遍历数组的地方,推荐使用以下方式代替:
- 传统 for 循环:
for (var i = 0; i < array.length; i++),仅访问数值索引,完全忽略原型上的属性。 - ES5 forEach:
array.forEach(function(item) {...}),同样只遍历自身索引属性。 - 现代 for...of:针对 ES6+ 环境,推荐使用
for...of,它直接迭代值且不受原型影响。
总结来说,除非你明确需要枚举对象的所有键(包括继承的可枚举属性),否则在遍历数组时应严格避开 for...in。
以上就是JavaScript技巧之不要用for in语句对数组进行遍历的详细内容,更多关于JavaScript遍历优化的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













