合并两个对象而不覆盖

时间:2013-12-15 01:12:31

标签: javascript jquery object merge

我有一个类似的defaultObject:

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

我还有另一个喜欢:

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

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

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

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

6 个答案:

答案 0 :(得分:10)

如果您对ES6感到满意:

Object.assign({}, default, values)

答案 1 :(得分:8)

对于那些不使用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 :(得分:5)

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

Object.assign({}, _default, values)

Object.assign

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


原始答案,写于2013年:

由于这是用jQuery标记的,因此您可以使用$.extend作为简单的跨浏览器解决方案

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

答案 3 :(得分:2)

另一种方法是简单地默认扩展值(而不是覆盖默认值的另一种方式)

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

这里需要注意的是,价值的内容也会发生变化。上面没有库是必需的,比上面的普通香草解决方案更容易。

答案 4 :(得分:1)

我基于Oriol的答案的版本添加了对数组的检查,以使数组不会转换为有趣的{'0':...,'1':...}东西

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;
}

答案 5 :(得分:0)

我发现,最简单的方法是使用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
  }
}