先上範例:
// Poisoning Object.prototype Object.prototype.bar = 1; var foo = {moo: 2}; for(var i in foo) { console.log(i); // prints both bar and moo }
這裡我們要注意兩點,一是 for in 迴圈會忽略 enumerable 設定為 false 的屬性。例如一個陣列的 length 屬性。第二是,由於 for in 會遍歷整個原型鏈,所以當原型鏈過長時,會對效能造成影響。
enumerable 是個很陌生的詞彙,實際上,你很難在 javascript 中發現它的影子,而它實際上也是作者從 ruby 中藉鑑而來的。創建 enumerable 的目的不是為了獨立使用,而是採用「混用」的方式,而 Prototype 中很多方法都混用了 enumerable,所以它可以說是 prototype 的奠基石。這裡不做詳細介紹,詳細內容可以參考 - Enumerable。
由於我們沒法改變for in 循環本身的行為,所以我們只能採取其他方法來過濾掉那些不希望出現在循環內的屬性,透過《Javascript學習筆記之對象篇(三) : hasOwnProperty》 我們知道hasOwnProperty 的方法是可以做到這一點。
使用 hasOwnProperty 過濾
仍使用上個範例:
// Poisoning Object.prototype Object.prototype.bar = 1; var foo = {moo: 2}; for(var i in foo) { if (foo.hasOwnProperty(i)) { console.log(i); } }
這是唯一正確的寫法,由於我們實用了 hasOwnProperty 方法,所以這次只輸出 moo。如果不適用 hasOwnProperty 方法,那麼當 Object.prototype 擴充時,就會出現錯誤。
現在很多框架都會選擇從 Object.prototype 擴充方法,所以當我們使用這些框架時,如果使用沒有用 hasOwnProperty 過濾的 for in 迴圈時就會遇到問題。
總結
建議養成 hasOwnProperty 過濾屬性的好習慣,不要對運行環境做任何假設,也無論原生的原型物件是否被擴充。