本文深入分析 JavaScript 中 for...in 语句的局限性,重点探讨其对内置属性、重写方法及原型扩展的遍历行为差异。通过对比 IE6-8 与现代浏览器的表现,揭示在跨浏览器开发中依赖 for...in 获取成员名称的风险,并建议使用 hasOwnProperty 进行过滤以确保代码兼容性。
JavaScript for...in 语句的缺陷总结
for...in 的基本行为与内置属性过滤
for...in 语句主要用于枚举对象的属性(即成员),示例代码如下。
var obj = { name:"jack",
getName:function(){return this.name}
};
//输出name,getName
for(var atr in obj) {
alert(atr);
}
需要注意的是,该循环不会输出 obj 的 toString、valueOf 等内置属性(也可称为内置成员、隐藏属性或预定义属性)。这意味着 for...in 仅用于列举对象的显示成员(即自定义成员)。
如果重写了内置属性,情况会发生变化。以下示例重写了 obj 的 toString 方法。
var obj = {name:"jack",
getName:function(){return this.name},
toString:function(){return "I'm jack."}
}
for(var atr in obj) {
alert(atr);
}
重写内置属性时的浏览器差异
此时会输出什么结果呢?
在 IE6、IE7 和 IE8 浏览器中,输出结果与未重写 toString 时相同,仅输出 name 和 getName。
而在 IE9、Firefox、Chrome、Opera 和 Safari 等现代浏览器中,则会输出 name、getName 以及 toString。
原型链扩展对遍历结果的影响
如果向内置原型中添加属性或方法,for...in 循环同样可以遍历到这些新增内容。
Object.prototype.clone = function() {}
var obj = {
name: 'jack',
age: 33
}
// name, age, clone
for (var n in obj) {
alert(n)
}
例如,给 Object.prototype 添加了 clone 方法后,所有浏览器在执行 for...in 时都会显示 clone。
这或许看起来影响不大,因为通常不建议扩展内置构造器的原型,这也是 Prototype.js 逐渐衰落的原因之一。jQuery 和 Underscore 并未扩展原型,前者在 jQuery 对象上操作,后者将所有方法挂载在下划线符号上。
兼容 ES5 时的跨浏览器陷阱
然而,有时为了兼容 ES5 或后续版本,我们不得不在不支持 ES5 的浏览器(如 IE6、IE7、IE8)中扩展内置构造器的原型。此时,for...in 在不同浏览器中的表现会出现差异,如下例所示。
if (!Function.prototype.bind) {
Function.prototype.bind = function(scope) {
var fn = this
return function () {
fn.apply(scope, arguments)
}
}
}
function greet(name) {
alert(this.greet + ', ' + name)
}
for (var n in greet) {
alert(n)
}
在 IE6、IE7 和 IE8 中会输出 bind,而其他现代浏览器则不会。这是因为现代浏览器原生支持 bind 方法,for...in 无法遍历到它;而在 IE6-8 中,则是通过给 Function.prototype 添加 bind 实现的。
总结而言,在跨浏览器设计中,不能依赖 for...in 来获取对象的成员名称,通常应使用 hasOwnProperty 进行判断。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!