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
复制代码
相关文章
相关标签/搜索