hasOwnProperty函數
為了判斷一個對象是否包含自定義屬性而不是原型鏈上的屬性, 我們需要使用繼承自 Object.prototype 的 hasOwnProperty 方法。
hasOwnProperty 是 JavaScript 中唯一一個處理屬性但是不查找原型鏈的函數。
注意: 通過判斷一個屬性是否undefined是不夠的。 因為一個屬性可能確實存在,只不過它的值被設置為undefined。
// 修改Object.prototype
Object.prototype.bar = 1;
var foo = {goo: undefined};
foo.bar; // 1
'bar' in foo; // true
foo.hasOwnProperty('bar'); // false
foo.hasOwnProperty('goo'); // true
只有 hasOwnProperty 可以給出正確和期望的結果,這在遍歷對象的屬性時會很有用。 沒有其它方法可以用來排除原型鏈上的屬性,而不是定義在對象自身上的屬性。
hasOwnProperty 作為屬性
JavaScript 不會保護 hasOwnProperty 被非法占用,因此如果一個對象碰巧存在這個屬性, 就需要使用外部的 hasOwnProperty 函數來獲取正確的結果。
var foo = {
hasOwnProperty: function() {
return false;
},
bar: 'Here be dragons'
};
foo.hasOwnProperty('bar'); // 總是返回 false
// 使用其它對象的 hasOwnProperty,并將其上下文設置為foo
({}).hasOwnProperty.call(foo, 'bar'); // true
結論
當檢查對象上某個屬性是否存在時,hasOwnProperty 是唯一可用的方法。 同時在使用 for in loop 遍歷對象時,推薦總是使用 hasOwnProperty 方法, 這將會避免原型對象擴展帶來的干擾。
for in 循環
和 in 操作符一樣,for in 循環同樣在查找對象屬性時遍歷原型鏈上的所有屬性。
注意: for in 循環不會遍歷那些 enumerable 設置為false 的屬性;比如數組的length 屬性。
// 修改 Object.prototype
Object.prototype.bar = 1;
var foo = {moo: 2};
for(var i in foo) {
console.log(i); // 輸出兩個屬性:bar 和 moo
}
由于不可能改變 for in 自身的行為,因此有必要過濾出那些不希望出現在循環體中的屬性, 這可以通過 Object.prototype 原型上的 hasOwnProperty
函數來完成。
使用 hasOwnProperty 過濾
注意: 由于 for in 總是要遍歷整個原型鏈,因此如果一個對象的繼承層次太深的話會影響性能。
// foo 變量是上例中的
for(var i in foo) {
if (foo.hasOwnProperty(i)) {
console.log(i);
}
}
這個版本的代碼是唯一正確的寫法。由于我們使用了 hasOwnProperty,所以這次只輸出 moo。 如果不使用 hasOwnProperty,則這段代碼在原生對象原型(比如 Object.prototype)被擴展時可能會出錯。
一個廣泛使用的類庫 Prototype 就擴展了原生的 JavaScript 對象。 因此,當這個類庫被包含在頁面中時,不使用 hasOwnProperty 過濾的 for in 循環難免會出問題。
總結
推薦總是使用 hasOwnProperty。不要對代碼運行的環境做任何假設,不要假設原生對象是否已經被擴展了。