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循環(huán)中遍歷出來或者在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” 賦值 或者 取值的時候會分別觸發(fā) 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 去遍歷數組?。?/p>
寫完收工!