javascript實現一個合並多個對象的方法


javascript實現合並對象的方法有很多種,比如:

1、Object.assign

2、jQuery.extend(jQuery也是用javascript寫的,謝謝)

3、lodash系列(lodash.merge、lodash.assign等,至於區別自己看文檔,文檔地址:https://lodash.com/docs)

4、Immutable.js(fackbook打造的一個不可變數據結構JS庫)的 merge 方法

 

其中,Object.assign為javascript原生方法,但是存在以下兩個在具體應用場景上的缺點:

1、瀏覽器兼容性問題

2、只能進行淺合並(關於淺合並深合並,碼友之述備矣,這里就不贅言,戳:https://juejin.im/entry/58df4c8b61ff4b006b131792

PS: 之所以說具體應用場景的缺點,是因為假如項目是在高版本瀏覽器運行,並且只要對數據結構進行淺合並,那就不存在上述兩個問題

 

而為了實現合並對象,特意引入上述的 jQuery、lodash、immutable這些庫,就有點誇張了(項目本身需要用到這些庫,那當我什么也沒說)

 

好了,進入正題,下面是我自己實現的一個可配置的合並多個對象的方法

function EXT(options) {
    return new EXT.prototype.init(options);
}

EXT.fn = EXT.prototype = {
    type: function(o) {
        return Object.prototype.toString.call(o).slice(8, -1).toLowerCase();
    },
    typeMap: {
        object: function() {
            return {};
        },
        array: function() {
            return [];
        }
    },
    // 默認配置項
    defaults: {
        // 是否深合並
        isDeep: true,
        // 是否遍歷合並源對象原型鏈上的屬性
        includePrototype: true,
        // 用於對每個合並項進行自定義修正
        forEach: function(target, name, sourceItem) {
            target[name] = sourceItem;
            return target;
        }
    },
    // 將配置項合並到默認配置項
    init: function(options) {
        for (let name in options) {
            this.defaults[name] = options[name];
        }
        return this;
    },
    merge: function() {
        let self = this,
            _default = self.defaults,
            i = 1,
            length = arguments.length,
            target = arguments[0] || {},
            source,
            targetItem,
            sourceItem,
            tiType,
            siType,
            clone,
            name;

        for (; i < length; i++) {
            // 判斷源對象是否為空
            if ((source = arguments[i]) != null) {
                for (name in source) {
                    // 是否遍歷源對象的原型鏈
                    if (source.hasOwnProperty(name) || _default.includePrototype) {
                        targetItem = target[name];
                        sourceItem = source[name];
                        tiType = self.type(targetItem);
                        siType = self.type(sourceItem);

                        // 防止出現回環
                        if (target === sourceItem) {
                            continue;
                        }

                        // 如果復制的是對象或者數組
                        if (_default.isDeep && sourceItem != null && self.typeMap[siType]) {
                            clone = targetItem != null && tiType === siType ? targetItem : self.typeMap[siType]();
                            // 遞歸
                            target[name] = self.merge(clone, sourceItem);
                        } else {
                            // 處理每一個合並項
                            target = _default.forEach.call(self, target, name, sourceItem);
                        }
                    }
                }
            }
        }
        return target;
    }
};
EXT.fn.init.prototype = EXT.fn;

 

擼個demo先,先定義兩份數據

function Foo() {
    this.a = 1;
}

function Bar() {
    this.c = 3;
}

Foo.prototype.b = 2;
Bar.prototype.d = 4;

let data = {
    info: {
        name: 'Leslie',
        age: 26,
        scores: [60, 66, 70, 80]
    }
};
let data2 = {
    info: {
        name: 'Leslie',
        age: 32,
        scores: [99, 66, 70, {
            name: 'john',
            age: 18
        },
        new Foo()]
    }
};

 1、普通合並

let target = EXT().merge(data1, data2);

結果為:

2、自定義配置進行合並

 isDeep:選擇是否進行深合並,設置為 false 則只進行淺合並,默認為 true

let target = EXT({ isDeep: false }).merge(data1, data2);
includePrototype:選擇是否要遍歷對象的原型鏈,默認為 true
let target = EXT({ includePrototype: false }).merge(data1, data2);

forEach:對每個合並項進行自定義處理

let target = EXT({
    forEach: function(target, name, sourceItem) {
        target[name] = sourceItem + ’hello, 自定義每個合並項‘;
        return target;
    }
}).merge(data1, data2);

 

好了,這個就是這個方法的使用方法,怎么樣?是不是很靈活?是不是很好用?

 

但是!(最怕的就是但是)這個方法還是有問題的,不知道電視機前的你發現沒有,就是合並 new Foo() 的時候,會把 Foo 原型鏈上的屬性一起拷貝到目標對象,而不是拷貝到目標對象的原型鏈

這個問題暫時沒想到解決方法,后面想到會更新上來,或者你有什么解決方案也可以留言,幫忙解決一下,最后祝大家加班愉快!

(胖虎是我偶像)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM