for in 对数组遍历的一个潜在bug是:如果有人在数组原型prototype添加了方法,或者引入了第三方js库,库中对数组原型进行了扩展,那么,for in 遍历数组时将会把这些原型方法也遍历出来,而for var 这种数组遍历并不会遍历出原型方法
举例
var x=[1];
for(var s in x){
alert(s);
};
====>输出:0
var x=[1];
Array.prototype.toJson = 'xxx';
for(var s in x){
alert(s);
};
===>输出:0,toJson
若使用for var遍历
var x=[1];
for(var i =0;i < x.length;i++){
alert(i);
};
====>输出:0
var x=[1];
Array.prototype.toJson = 'xxx';
for(var i =0;i < x.length;i++){
alert(i);
};
====>输出:0
由此可见for in遍历数组会出现遍历出原型方法的bug
如果坚持想用for in遍历数组,可以考虑用ECMAScript5中的defineProperty方法来给数组原型上扩展,不过:1:defineProperty方法由于是ES5的,所以不支持IE9以下的浏览器 2:必须确保所有人都用这个方法来给数组进行扩展,要不遍历就会出现问题
关于defineProperty:
这个新方法很厉害了,vue.js和avalon.js 都是通过它实现双向绑定的,所以在这里有必要介绍一下
事例:
var a= {}
Object.defineProperty(a,"b",{
value:123
})
console.log(a.b);//123
这个方法接收三个参数
第一个:目标对象
第二个:需要定义的属性或者方法名
第三个:目标属性所拥有的特性(descriptor)
这里主要介绍下第三个参数
value: 属性的值
writable: true(属性的值可以重写),false(属性值不能重写,只能读)
configurable: 总开关,如果为false, 就不能再设置他的(value,writable,configurable)
enumerable: 是否能在for in循环中遍历出来或者在Object.keys中列举出来。
get: 下面详解
set: 下面详解
知识点1:
如果未对writable,configurable,enumerable进行设置,会自动默认为false的初始值
知识点2:configurable
var a= {}
Object.defineProperty(a,"b",{
configurable:false
})
Object.defineProperty(a,"b",{
configurable:true
})
//error: Uncaught TypeError: Cannot redefine property: b
就会报错了。。注意上面讲的默认值。。。如果第一次不设置它会帮你设置为false。。所以。。第二次。再设置他会报错
知识点3:writable
知识点4:enumerable
这个是和我们上面介绍的for in 遍历数组有关的解决方案
enumerable设置为fasle,将会禁止枚举在defineProperty中定义的方法
set 和 get
在 descriptor 中不能 同时设置访问器 (get 和 set) 和 wriable 或 value,否则会错,就是说想用(get 和 set),就不能用(wriable 或 value中的任何一个)
var a= {}
Object.defineProperty(a,"b",{
set:function(newValue){
console.log("你要赋值给我,我的新值是"+newValue)
},
get:function(){
console.log("你取我的值")
return 2 //注意这里,我硬编码返回2
}
})
a.b = 1 //打印 你要赋值给我,我的新值是1
console.log(a.b) //打印 你取我的值
//打印 2 注意这里,和我的硬编码相同的
简单来说,, 这个 “b” 赋值 或者 取值的时候会分别触发 set 和 get 对应的函数
除了通过defineProperty,还有一种方法可以避免遍历出原型链方法:hasOwnProperty
可以用 hasOwnProperty 来确定这个属性名是该对象的成员还是来自于原型链,如果对象拥有独有的属性,它将返回true,
hasOwnProperty 方法不会检查原型链;
var obj = {
name: 'wang',
age: 21,
getAge: function(){ },
getName: function(){}
}
for(var i in obj){
if( obj.hasOwnProperty(i) ){
console.log(i)
}
}
这样,将会过滤掉原型链中的方法和属性
虽然可以通过这两种方法避免遍历出原型链方法,但是还是强烈建议不要使用for in 去遍历数组!!
写完收工!