JS避坑-如何優雅地遍歷對象

雖然已知for...in會遍歷對象原型鏈上的屬性,但心想用於字面量建立的對象應該沒有什麼大問題,因而便棄hasOwnProperty於不顧,最終被工單教作人了。在修復中總結了幾個可讓遍歷對象更加優雅的方法。數組

for...in踩坑覆盤

能不能去掉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方法,爲什麼報錯呢?緣由是用戶另引入的腳本對ObjectArrayString等基礎引用類型的原型鏈作了擴展,加入了一些方法。因此在for...in遍歷renderLayers時,也遍歷到了addRangeclear這些屬性,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

1. 數組儘可能使用forEach進行遍歷

好比引起報錯的這一段,renderLayers是一個數組,直接使用forEach進行遍歷便可:調試

renderLayers.filter(layer => !layer.isHidden()).forEach(layer => {
    // ...
});
複製代碼

2. 對象深拷貝儘可能使用解構賦值

for...in可遍歷對象屬性實現一一賦值完成簡單的對象深拷貝,這種操做能夠用解構賦值來實現,更簡單。code

function copy(obj) {
    return {...obj};
}

const obj = {
    a: 1
};
const objCopy = copy(obj);
console.log(objCopy);
// 輸出:{a: 1}
複製代碼

3. 遍歷鍵值能夠結合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
複製代碼

4. 若要使用breakreturn提早結束循環,需結合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
複製代碼
相關文章
相關標籤/搜索