js對象的核心是一個字元串屬性名與屬性值的映射表。使用對象實現字典易如反掌,字典是可變長的字元串與值的映射集合。 for...in js提供了枚舉一個對象屬性名的利器--for...in迴圈。var dict={zhangsan:34,lisi:24,wangwu:62}; var people=[... ...
js對象的核心是一個字元串屬性名與屬性值的映射表。使用對象實現字典易如反掌,字典是可變長的字元串與值的映射集合。
for...in
js提供了枚舉一個對象屬性名的利器--for...in迴圈。
var dict={zhangsan:34,lisi:24,wangwu:62};
var people=[];
for(var name in dict){
people.push(name+":"+dict[name]);
}
people;//["zhangsan:34", "lisi:24", "wangwu:62"]
使用自定義對象
但一些自定義的對象還會繼承其原型對象中的屬性,for...in迴圈除了枚舉"自身"屬性外,原型鏈中的屬性也不會放過。下麵看一下例子
function NaiveDict(){
}
NaiveDict.prototype.count=function(){
var i=0;
for(var name in this){
i++;
}
return i;
};
NaiveDict.prototype.toString=function(){
return '[Object NaiveDict]';
};
var dict=new NaiveDict();
dict.zhangsan=24;
dict.lisi=34;
dict.wangwu=62;
dict.count();//5
上面的代碼,實例化一個dict對象,並添加了私有屬性(zhangsan,lisi,wangwu)並繼承了原型對象的方法(toString,count),所以當調用count方法時,這個時候的for...in迴圈,不僅枚舉了了私有屬性也枚舉了原型對象的方法屬性(zhangsan,lisi,wangwu,toString,count)。
使用數組類型
利用js的靈活性,我們可以給任意的類型的對象添加屬性,因此給數組添加屬性似乎能工作。
var dict=new Array();
dict.zhangsan=24;
dict.lisi=34;
dict.wangwu=62;
dict.zhangsang;//24
當我們給數組的原型對象添加一些原型方法,也就是前面講的猴子補丁ployfill的使用時,這個時候我們再枚舉上面的dict對象的屬性時,錯誤就出現了。
Array.prototype.first=function(){
return this[0];
};
Array.prototype.last=function(){
return this[this.length-1];
};
Array.prototype.eq=function(idx){
if(idx<0)idx=this.length+idx;
return this[idx];
};
運行一下枚舉
var names=[];
for(var name in dict){
names.push(name);
}
names;//["zhangsan", "lisi", "wangwu","first","last"]
輕量級字典的正確姿勢
首要原則
應該僅僅將Object的直接實例作為字典,而不是子類,或其它對象。
上面的數組的例子可以改成下麵這些的代碼
var dict={};
dict.zhangsan=24;
dict.lisi=34;
dict.wangwu=62;
dict.zhangsang;
var names=[];
for(var name in dict){
names.push(name);
}
names;//["zhangsan", "lisi", "wangwu"]
需要註意
還是無法避免對於Object.prototype對象的修改,但可以將風險僅僅局限在Object.prototype。
優點
雖說所有人都可以修改Object.prototype對象,也會對for...in迴圈造成影響。但相比之下,增加屬性到Array.prototype中是合理的。如之前給不支持數組標準方法的環境中將這些方法增加到Array.prototype中。這些屬性也會導致for...in迴圈,堅持Object的直接實例原則,可以使得for...in迴圈擺脫原型污染的影響。對於構建行為正確的字典,這是個必要非充分條件。
提示
-
使用對象字面量構建輕量級字典
-
輕量級字典應該是Object.prototype的直接子類,以使for...in迴圈免受原型的污染
附錄:for...in
以下內容來自:Mozilla 開發者社區
以任意序迭代一個對象的可枚舉屬性。每個不同的屬性,語句都會被執行一次。
語法
for(variable in object){...}
參數
-
variable:每次迭代,一個不同的屬性名將賦予variable
-
object:可枚舉屬性被迭代的對象
描述
-
只言遍歷可枚舉屬性。
-
迴圈將迭代對象的所有可枚舉屬性和從它的構造函數的prototype繼承而來的
刪除、添加或修改屬性
for...in迴圈以任意序迭代一個對象的屬性。如果一個屬性在一次迭代中被修改,在稍後被訪問,其在迴圈中的值是其在稍後時間的值。一個在被訪問之前已經被刪除的屬性將不會在之後被訪問。在迭代進行時被添加到對象的屬性,可能在之後的迭代被訪問,也可能被忽略。通常,在迭代過程中最好不要在對象上進行添加、修改或刪除屬性的操作,除非是對當前正在被訪問的屬性。因為訪問的無序性,都無法保證,添加、修改或刪除的屬性,會在之後被訪問。
Array迭代和for...in
數組索引是可枚舉的整數名,其他方面和普通對象屬性沒有區別。for...in的無序性,無法保證枚舉是按索引順序進行,但會返回所有可枚舉的屬性,包括非整數名稱和繼承的屬性。
如果次序很重要,可以使用for迴圈來迭代數組。
僅迭代自身的屬性
只需要對象本身的屬性,不包括它繼承來的。可以使用getOwnPropertyNames()或執行hasOwnProperty()來確定某屬性是否是對象自身的。
例子
返回一個對象的所有可枚舉屬性
var obj={a:2,b:1,c:30};
for(var prop in obj){
console.log('obj.'+prop+'='+obj[prop]);
}
// "obj.a = 2"// "obj.b = 1"// "obj.c = 30"
只返回對象自身的可枚舉屬性
var triangle = {a:1, b:2, c:3};
function ColoredTriangle() {
this.color = "red";
}
ColoredTriangle.prototype = triangle;
var obj = new ColoredTriangle();
for (var prop in obj) {
if( obj.hasOwnProperty( prop ) ) {
console.log("o." + prop + " = " + obj[prop]);
}
}
// "o.color = red"