• 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 原型链上的属性一起拷贝到目标对象,而不是拷贝到目标对象的原型链

    这个问题暂时没想到解决方法,后面想到会更新上来,或者你有什么解决方案也可以留言,帮忙解决一下,最后祝大家加班愉快!

    (胖虎是我偶像)

  • 相关阅读:
    d
    今天刚注册,测试下
    关于Hadoop的简单介绍
    leveldb 阅读笔记 (2) 简易测试框架
    leveldb 阅读笔记(1) 内存分配器 Arena
    数论
    伴随网站
    convenience website
    大佬独特的骗分技巧
    动态规划
  • 原文地址:https://www.cnblogs.com/LeslieHoward/p/8544166.html
Copyright © 2020-2023  润新知