雖然已知for...in會遍歷對象原型鏈上的屬性,但心想用於字面量建立的對象應該沒有什麼大問題,因而便棄hasOwnProperty於不顧,最終被工單教作人了。在修復中總結了幾個可讓遍歷對象更加優雅的方法。數組
hasOwnProperty
?for...in
經常使用於遍歷對象或者數組,好比bash
const obj = { a: 1, b: 2 }; for (const key in obj) { console.log(key, obj[key]); } // 輸出 // a 1 // b 2 複製代碼
咱們都知道for...in
會遍歷原型鏈上的屬性,因此通常會結合hasOwnProperty
來判斷屬性是否在對象自身上,而不是在原型鏈上。markdown
for (const key in obj) { if (obj.hasOwnProperty(key)) { console.log(key, obj[key]); } } 複製代碼
可每次都要多增長一行代碼,多一個縮進,實在麻煩,能不能偷懶不加hasOwnProperty? 因而我動起了當心思,使用字面量建立的對象或者數組,不是類的實例,原型鏈上乾乾淨淨的,那遍歷的時候也不必判斷了吧。因而就在遍歷字面量對象時放心大膽地把hasOwnProperty
拋棄了。函數
我負責的產品是API,用戶在本身的頁面應用中引入使用。某天,有用戶反饋若同時引入我家API和另外一個腳本庫就會引起報錯:工具
調試發現報錯發生在:this
for (const key in renderLayers) { const layer = renderLayers[key]; if (!layer.isHidden()) { // ... } } 複製代碼
renderLayers
是一個數組,這裏本來是遍歷可渲染圖層進行操做,而圖層對象都有isHidden
方法,爲什麼報錯呢?緣由是用戶另引入的腳本對Object
、Array
、String
等基礎引用類型的原型鏈作了擴展,加入了一些方法。因此在for...in
遍歷renderLayers
時,也遍歷到了addRange
、clear
這些屬性,layer
則賦值爲一個function
,而不是圖層對象。spa
Object.extend = function(dest, source, replace) { for(var prop in source) { if(replace == false && dest[prop] != null) { continue; } dest[prop] = source[prop]; } return dest; }; Object.extend(Array.prototype, { addRange: function(items) { if(items.length > 0) { for(var i=0; i < items.length; i++) { this.push(items[i]); } } }, clear: function() { this.length = 0; return this; }, // ... }, false); 複製代碼
如上所述,只能開始內部大清理,全部使用for...in
而沒有帶hasOwnProperty
的地方都須要進行改造。除了加上hasOwnProperty
進行判斷以外,視具體狀況還可使用如下方法,讓你的代碼更加優雅:prototype
forEach
進行遍歷好比引起報錯的這一段,renderLayers
是一個數組,直接使用forEach
進行遍歷便可:調試
renderLayers.filter(layer => !layer.isHidden()).forEach(layer => {
// ...
});
複製代碼
for...in
可遍歷對象屬性實現一一賦值完成簡單的對象深拷貝,這種操做能夠用解構賦值來實現,更簡單。code
function copy(obj) { return {...obj}; } const obj = { a: 1 }; const objCopy = copy(obj); console.log(objCopy); // 輸出:{a: 1} 複製代碼
Object.entries()
和forEach
Object.entries()
返回對象全部鍵值對組成的數組,再結合forEach
便可完成遍歷。若只是遍歷對象的鍵或者值,可使用Object.keys()
和Object.values()
。 在改造過程當中,能夠抽象出一個forIn
方法做爲工具函數,這樣多處調用就能夠省掉很多冗餘代碼啦~
function forIn(obj, callback) { Object.entries(obj).forEach(entry => { callback(...entry); }); } const obj = { a: 1 }; forIn(obj, (key, value) => { console.log(key, value); }); // 輸出:a 1 複製代碼
break
、return
提早結束循環,需結合for...of
方法3雖好,但使用forEach
沒辦法中斷循環,這時候可使用for...of
,也是很是簡潔的。
const obj = { a: 1, b: 2 }; for (let [key, value] of Object.entries(obj)) { if (value > 1) { break; } console.log(key, value); } // 輸出:a 1 複製代碼