【问题标题】:Merge two objects without override合并两个对象而不覆盖
【发布时间】:2014-01-02 14:51:51
【问题描述】:

我有一个这样的默认对象:

var default = {
    abc: "123",
    def: "456",
    ghi: {
       jkl: "789",
       mno: "012"
    }
};

我还有一个喜欢的:

var values = {
    abc: "zzz",
    ghi: {
       jkl: "yyy",
    }
};

如何将这 2 个对象与以下结果合并(不覆盖)?

var values = {
    abc: "zzz",
    def: "456",
    ghi: {
       jkl: "yyy",
       mno: "012"
    }
};

(我不想更改默认对象!)

【问题讨论】:

    标签: javascript jquery object merge


    【解决方案1】:

    对于那些不使用 jQuery 的人来说,这里有一个 vanilla-js 解决方案。

    解决方案

    function extend (target) {
        for(var i=1; i<arguments.length; ++i) {
            var from = arguments[i];
            if(typeof from !== 'object') continue;
            for(var j in from) {
                if(from.hasOwnProperty(j)) {
                    target[j] = typeof from[j]==='object'
                    ? extend({}, target[j], from[j])
                    : from[j];
                }
            }
        }
        return target;
    }
    

    压缩(使用Closure Compiler):

    只有 199 个字符!

    var extend=function e(c){for(var d=1;d<arguments.length;++d){var a=arguments[d];if("object"===typeof a)for(var b in a)a.hasOwnProperty(b)&&(c[b]="object"===typeof a[b]?e({},c[b],a[b]):a[b])}return c}
    

    如何使用

    extend(target, obj1, obj2); // returns target
    

    如果你只想合并,使用

    var merged = extend({}, obj1, obj2);
    

    特点

    • 它不查看对象的原型。
    • 忽略非对象。
    • 递归是为了合并对象属性。
    • target 的属性中引用的对象,如果扩展,将被新对象替换,而原始对象不会被修改。
    • 如果属性名称相同,合并值将是最后一个(按参数顺序)非对象值之后的对象的合并。或者,如果最后一个不是对象,则它本身。

    示例

    extend({}, {a:1}, {a:2});            // {a:2}
    extend({}, {a:1}, {b:2});            // {a:1, b:2}
    extend({}, {a: {b:1}}, {a: {b:2}});  // {a: {b:2}}
    extend({}, {a: {b:1}}, {a: {c:2}});  // {a: {b:2, c:2}}
    extend({}, {a: {a:1}}, {a: {b:2}}, {a: 'whatever non object'});
        // {a: "whatever non object"}
    extend({}, {a: {a:1}}, {a: {b:2}}, {a: 'whatever non object'}, {a: {c:3}},{a: {d:4}});
        // {a: {c:3, d:4}}
    

    警告

    请注意,如果浏览器不够聪明,它可能会陷入无限循环:

    var obj1={},
        obj2={};
    obj1.me=obj1;
    obj2.me=obj2;
    extend({},obj1,obj2);
    

    如果浏览器足够聪明,它可以抛出错误,或者返回{me: undefined},或者其他什么。

    请注意,如果您使用 jQuery 的 $.extend,此警告也适用。

    【讨论】:

      【解决方案2】:

      现在所有现代浏览器都支持 ES2015,原生 Object.assign 可用于扩展对象

      Object.assign({}, _default, values)
      

      Object.assign

      注意default是保留关键字,不能用作变量名


      原始答案,写于 2013 年:

      由于这是用 jQuery 标记的,您可以使用 $.extend 来获得简单的跨浏览器解决方案

      var temp = {};
      $.extend(true, temp, _default, values);
      values = temp;
      

      【讨论】:

      • 但这会改变我的默认'default_array',我不想要它,因为它是一个全局变量......
      • @amp - 这很容易通过使用临时对象来避免。
      • 这个不行,ghi会被覆盖,不会合并在一起。
      • 是的,正在嵌套的 json 中删除现有对象
      【解决方案3】:

      如果你对 ES6 满意的话:

      Object.assign({}, default, values)
      

      【讨论】:

      • 如果默认值存在,则仍然覆盖
      【解决方案4】:

      另一种方法是在默认情况下简单地扩展值(而不是覆盖默认值的其他方式)

      Object.assign(value, default) // values of default overrides value
      default = value // reset default to value
      

      这里需要注意的是 value 的内容也发生了变化。好处是不需要库,并且比上面的普通解决方案更容易。

      【讨论】:

        【解决方案5】:

        我基于 Oriol 的回答的版本添加了对数组的检查,这样数组就不会变成有趣的 {'0': ..., '1': ...} thingys

        function extend (target) {
          for(var i=1; i<arguments.length; ++i) {
            var from = arguments[i];
            if(typeof from !== 'object') continue;
            for(var j in from) {
              if(from.hasOwnProperty(j)) {
                target[j] = typeof from[j]==='object' && !Array.isArray(from[j])
                  ? extend({}, target[j], from[j])
                  : from[j];
              }
            }
          }
          return target;
        }
        

        【讨论】:

          【解决方案6】:

          我发现最简单的方法是使用来自 lodash (https://lodash.com/docs/4.17.15#mergeWith) 的 mergeWith(),它接受一个自定义函数,该函数决定如何处理每个属性合并。这是递归的:

          const mergeFunction = (objValue, srcValue) => {
            if (typeof srcValue === 'object') {
              _.mergeWith(objValue, srcValue, mergeFunction)
            } else if (objValue) {
              return objValue
            } else {
              return srcValue
            }
          }
          

          【讨论】:

            猜你喜欢
            • 2011-11-24
            • 2018-04-22
            • 2017-11-27
            • 1970-01-01
            • 1970-01-01
            • 2020-04-21
            • 2018-05-13
            • 2017-09-22
            • 2019-10-04
            相关资源
            最近更新 更多