如何动态合并两个JavaScript对象的属性?


2516

我需要能够在运行时合并两个(非常简单的)JavaScript对象。例如,我想:

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }

obj1.merge(obj2);

//obj1 now has three properties: food, car, and animal

有没有人为此提供脚本或是否知道内置方法?我不需要递归,也不需要合并函数,只需合并平面对象上的方法即可。


值得一提的是,在类似的问题上回答了这个问题,该问题说明了如何“向下合并”。也就是说,它合并了重复键的值(而不是用第二个值覆盖第一个值),但是没有递归地进行重复。恕我直言,它很好的清洁代码完成该任务。
ToolmakerSteve

顺便说一句,最上面的几个答案会进行“浅”合并:如果obj1和obj2中都存在相同的键,则obj2中的值将保留,而obj1中的值将被删除。例如,如果有问题的示例var obj2 = { animal: 'dog', food: 'bone' };,则合并为{ food: 'bone', car: 'ford', animal: 'dog' }。如果您正在使用“嵌套数据”,并希望进行“深度合并”,请查找提及“深度合并”或“递归”的答案。如果你有一个是价值观arrays,然后使用github上的“arrayMerge”选项“TehShrike / deepmerge”,提到这里
制造商史蒂夫(Steve),

请点击下面的链接 stackoverflow.com/questions/171251/… 作为ES6版本的新功能的点差运算符
Dev216

Answers:


2903

ECMAScript 2018标准方法

您将使用对象传播

let merged = {...obj1, ...obj2};

merged现在是obj1和的并集obj2。中的属性obj2将覆盖中的属性obj1

/** There's no limit to the number of objects you can merge.
 *  Later properties overwrite earlier properties with the same name. */
const allRules = {...obj1, ...obj2, ...obj3};

这也是此语法的MDN文档。如果您正在使用babel,则需要babel-plugin-transform-object-rest-spread插件才能正常工作。

ECMAScript 2015(ES6)标准方法

/* For the case in question, you would do: */
Object.assign(obj1, obj2);

/** There's no limit to the number of objects you can merge.
 *  All objects get merged into the first object. 
 *  Only the object in the first argument is mutated and returned.
 *  Later properties overwrite earlier properties with the same name. */
const allRules = Object.assign({}, obj1, obj2, obj3, etc);

(请参阅MDN JavaScript参考


ES5和更早版本的方法

for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }

请注意,这会简单地添加的所有属性obj2,以obj1这可能不是你想要什么,如果你仍然想使用未修改obj1

如果您使用的框架覆盖了您的所有原型,那么您必须对诸如的检查更为精通hasOwnProperty,但是该代码将在99%的情况下都能正常工作。

示例功能:

/**
 * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
 * @param obj1
 * @param obj2
 * @returns obj3 a new object based on obj1 and obj2
 */
function merge_options(obj1,obj2){
    var obj3 = {};
    for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
    for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
    return obj3;
}

90
如果对象具有相同的名称属性,那么这将不起作用,并且您还希望合并属性。
谢雷(YièJìléi)2010年

69
这只会执行浅复制/合并。有可能破坏很多元素。
杰伊·泰勒

45
+1表示承认某些可怜的人被迫使用在整个原型中都乱七八糟的框架...
thejonwithnoh

4
这对我不起作用,因为“因此,它分配属性而不是仅复制或定义新属性。如果合并源包含getter,这可能使其不适合将新属性合并到原型中。” (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…)。我不得不用var merged = {...obj1, ...obj2}
morgler

2
@ Ze'ev{...obj1, ...{animal: 'dog'}}
backslash112

1190

jQuery对此也有一个实用程序:http : //api.jquery.com/jQuery.extend/

取自jQuery文档:

// Merge options object into settings object
var settings = { validate: false, limit: 5, name: "foo" };
var options  = { validate: true, name: "bar" };
jQuery.extend(settings, options);

// Now the content of settings object is the following:
// { validate: true, limit: 5, name: "bar" }

上面的代码将更改名为的现有对象settings


如果要创建新对象而不修改任何一个参数,请使用以下命令:

var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };

/* Merge defaults and options, without modifying defaults */
var settings = $.extend({}, defaults, options);

// The content of settings variable is now the following:
// {validate: true, limit: 5, name: "bar"}
// The 'defaults' and 'options' variables remained the same.

166
注意:变量“设置”将被修改。jQuery不返回新实例。这样做(以及命名)的原因是开发了.extend()来扩展对象,而不是将东西塞在一起。如果您想要一个新对象(例如,您不想触摸的默认设置),则始终可以使用jQuery.extend({},设置,选项);
webmat 2011年

1
对!谢谢你 如先前的评论所述,它的名字很差。我搜索了jQuery文档的第四和第四部分,但没有发现。
Mike Starov

28
请注意,jQuery.extend也具有较深的(布尔)设置。jQuery.extend(true,settings,override),如果设置中的属性包含一个对象并且重写仅包含该对象的一部分,则这一点很重要。不会删除不匹配的属性,深度设置只会更新存在的位置。默认为false。
vol7ron 2011年

19
哎呀,他们在命名方面确实做得很好。如果您搜索如何扩展Javascript对象,它将立即出现!但是,如果您尝试将Javascript对象融合或缝合在一起,则可能无法找到它。
德里克·格里尔

2
当几行香草JS可以完成他们想要的操作时,不要告诉人们考虑使用库方法。jQuery出现了一段时间!
CrazyMerlin '16

346

和谐的ECMAScript 2015年(ES6)指定Object.assign将做到这一点。

Object.assign(obj1, obj2);

当前的浏览器支持越来越好,但是如果您正在开发不支持的浏览器,则可以使用polyfill


35
请注意,这只是一个浅层合并
Joachim Lous

我认为与此同时Object.assign覆盖面也不错:kangax.github.io/compat-table/es6/…–
LazerBass

13
现在,这应该是正确的答案。如今,人们将其代码编译为与不支持这种功能的罕见浏览器(IE11)兼容。旁注:如果您不想将obj2添加到obj1,则可以使用Object.assign({}, obj1, obj2)
Duvrai

6
@Duvrai据我所见,截至2016
。– NanoWizard

3
@Kermani OP特别指出了递归是不必要的。因此,虽然知道此解决方案执行浅合并很有用,但此答案就足够了。JoachimLou的评论已当之无愧,并在需要时提供了更多信息。我认为深度和浅层合并与类似主题之间的区别超出了本讨论的范围,并且更适合于其他SO问题。
NanoWizard

264

我用谷歌搜索代码来合并对象属性,并在这里结束。但是,由于没有任何递归合并代码,所以我自己编写了它。(也许jQuery扩展是递归BTW吗?)无论如何,希望其他人也会发现它也有用。

(现在代码不使用 Object.prototype:)

/*
* Recursively merge properties of two objects 
*/
function MergeRecursive(obj1, obj2) {

  for (var p in obj2) {
    try {
      // Property in destination object set; update its value.
      if ( obj2[p].constructor==Object ) {
        obj1[p] = MergeRecursive(obj1[p], obj2[p]);

      } else {
        obj1[p] = obj2[p];

      }

    } catch(e) {
      // Property in destination object not set; create it and set its value.
      obj1[p] = obj2[p];

    }
  }

  return obj1;
}

一个例子

o1 = {  a : 1,
        b : 2,
        c : {
          ca : 1,
          cb : 2,
          cc : {
            cca : 100,
            ccb : 200 } } };

o2 = {  a : 10,
        c : {
          ca : 10,
          cb : 20, 
          cc : {
            cca : 101,
            ccb : 202 } } };

o3 = MergeRecursive(o1, o2);

产生像o3的对象

o3 = {  a : 10,
        b : 2,
        c : {
          ca : 10,
          cb : 20,
          cc : { 
            cca : 101,
            ccb : 202 } } };

11
很好,但是我将首先对这些对象进行深度复制。当对象通过引用传递时,o1的这种方式也将被修改。
skerit 2011年

1
这就是我想要的。在转到try catch语句之前,请确保检查obj2.hasOwnProperty(p)。否则,您可能最终会从原型链的更高层合并到其他废话中。
亚当

3
谢谢马库斯。注意,o1也由MergeRecursive修改,因此最后,o1和o3相同。如果不返回任何内容,可能会更直观,因此用户知道他们提供的内容(o1)已被修改并成为结果。另外,在您的示例中,可能值得向o2中添加一些本来不在o1中的东西,以表明o2属性被插入到o1中(下面的其他人提交了复制代码来复制您的示例,但是当o2包含未包含的属性时它们会中断)在o1中-但您的工作在此方面)。
pancake 2012年

7
这是一个很好的答案,但有一些疑问:1)逻辑不应基于异常;在这种情况下,任何抛出的异常都将被捕获,导致不可预测的结果。2)我同意@pancake关于不返回任何内容的评论,因为原始对象已被修改。这是一个没有异常逻辑的jsFiddle示例:jsfiddle.net/jlowery2663/z8at6knn/4 – Jeff Lowery
Jeff Lowery

3
另外,在存在对象数组的任何情况下,都需要obj2 [p] .constructor == Array。
作曲家2015年

175

请注意,underscore.jsextend-method可以做到这一点:

_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}

36
这个例子很好,因为您正在处理匿名对象,但是如果不是这种情况,那么jQuery回答中有关突变的警告中webmat的注释就适用于此,因为下划线还会使目标对象发生突变。像jQuery的答案一样,要做到无突变,只需将其合并为一个空对象即可:_({}).extend(obj1, obj2);
Abe Voelker,2012年

8
还有另一种可能与您要实现的目标有关: _.defaults(object, *defaults)“使用默认对象中的值填充对象中的null和未定义属性,然后返回该对象。”
conny 2012年

许多人都评论过要更改目标对象,但是与其使用一种方法来创建一个新方法,不如使用一种通用模式(例如,在dojo中)是说dojo.mixin(dojo.clone(myobj),{newpropertys:“添加to myobj“})
Colin D

7
下划线可以使用任意数量的对象,因此可以通过执行操作避免原始对象发生突变var mergedObj = _.extend({}, obj1, obj2)
lobati

84

与jQuery extend()类似,您在AngularJS中具有相同的功能:

// Merge the 'options' object into the 'settings' object
var settings = {validate: false, limit: 5, name: "foo"};
var options  = {validate: true, name: "bar"};
angular.extend(settings, options);

1
@naXa我想如果您不想为此fn添加一个lib,那将是一个选择。
juanmf '16

67

我今天需要合并对象,这个问题(和答案)对我很有帮助。我尝试了一些答案,但都不满足我的需求,因此我合并了一些答案,自己添加了一些内容,并提出了新的合并功能。这里是:

var merge = function() {
    var obj = {},
        i = 0,
        il = arguments.length,
        key;
    for (; i < il; i++) {
        for (key in arguments[i]) {
            if (arguments[i].hasOwnProperty(key)) {
                obj[key] = arguments[i][key];
            }
        }
    }
    return obj;
};

一些示例用法:

var t1 = {
    key1: 1,
    key2: "test",
    key3: [5, 2, 76, 21]
};
var t2 = {
    key1: {
        ik1: "hello",
        ik2: "world",
        ik3: 3
    }
};
var t3 = {
    key2: 3,
    key3: {
        t1: 1,
        t2: 2,
        t3: {
            a1: 1,
            a2: 3,
            a4: [21, 3, 42, "asd"]
        }
    }
};

console.log(merge(t1, t2));
console.log(merge(t1, t3));
console.log(merge(t2, t3));
console.log(merge(t1, t2, t3));
console.log(merge({}, t1, { key1: 1 }));

3
好的答案,但我认为如果第一个和第二个中存在某个属性,并且您尝试通过合并第一和第二个属性来创建新对象,那么第一个对象中存在的唯一属性将丢失
Strikers 2015年

2
但这不是预期的行为吗?该函数的目的是按参数顺序覆盖值。下一个将覆盖当前。您如何保留独特的价值?从我的例子中,您有什么期望merge({}, t1, { key1: 1 })
Emre Erkan 2015年

我的期望是仅合并非对象类型的值。
AGamePlayer


对于我的node.js项目,这在严格模式下对我有用。到目前为止,这是此帖子的最佳答案。
klewis

48

您可以使用对象传播属性 -当前是第3阶段ECMAScript提议。

const obj1 = { food: 'pizza', car: 'ford' };
const obj2 = { animal: 'dog' };

const obj3 = { ...obj1, ...obj2 };
console.log(obj3);


浏览器支持?
Alph.Dev'17年

1
@ Alph.Dev您知道caniuse.com吗?
connexo

我无法在node.js中使用3点语法。节点对此抱怨。
klewis

41

给出的解决方案应进行修改,以检查source.hasOwnProperty(property)for..in分配前环-否则,你最终会复制整个原型链,这是很少所需的属性...


40

在一行代码中合并N个对象的属性

一种Object.assign方法是在2015年的ECMAScript(ES6)标准的一部分,不正是你需要的。(IE不支持)

var clone = Object.assign({}, obj);

Object.assign()方法用于将所有可枚举的自身属性的值从一个或多个源对象复制到目标对象。

阅读更多...

填充工具支持旧版浏览器:

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}

在较旧的浏览器中“严格使用”有什么用?为什么呢?支持object [defineProperty;的浏览器 钥匙; getOwnPropertyDescriptor; 等]并不完全是旧的。它们只是第5代UA的早期版本。
Bekim Bacaj

感谢您澄清Objects.assign返回的合并对象,而其他答案则不会。那是更具功能性的编程风格。
Sridhar Sarnobat

36

以下两个可能是一个很好的起点。lodash还具有满足这些特殊需求的定制器功能!

_.extendhttp://underscorejs.org/#extend
_.mergehttps://lodash.com/docs#merge


4
请注意,上述方法会变异原始对象。
Wtower

lodash方法对我有用,因为我想合并两个对象并保留嵌套的深度超过一到两个级别(而不是仅仅替换/擦除它们)。
SamBrick

真的@Wtower。因此遇到了一个错误。最好确保它总​​是像_.merge({},...一样开始
马丁·克里默

29

顺便说一句,您正在做的是覆盖属性,而不是合并...

这就是JavaScript对象区域真正合并的方式:只有to对象中不是对象本身的键才会被覆盖from。其他所有内容都将真正合并。当然,您可以更改此行为以不覆盖仅当to[n] is undefined,等时存在的任何内容:

var realMerge = function (to, from) {

    for (n in from) {

        if (typeof to[n] != 'object') {
            to[n] = from[n];
        } else if (typeof from[n] == 'object') {
            to[n] = realMerge(to[n], from[n]);
        }
    }
    return to;
};

用法:

var merged = realMerge(obj1, obj2);

3
下划线js扩展确实帮了我很多忙,而不是合并了
David Cumps 2014年

1
值得一提的是,typeof数组和typeof null将返回'object'并将其破坏。
萨拉卡

@ u01jmg3在这里看到我的答案:stackoverflow.com/questions/27936772/…-isObject函数
Salakar

如果在node.js项目的严格模式下使用,则会中断
klewis

28

这是我的刺

  1. 支持深度合并
  2. 不改变参数
  3. 接受任意数量的参数
  4. 不扩展对象原型
  5. 不依赖于另一个库(jQueryMooToolsUnderscore.js等)
  6. 包括对hasOwnProperty的检查
  7. 是短 :)

    /*
        Recursively merge properties and return new object
        obj1 &lt;- obj2 [ &lt;- ... ]
    */
    function merge () {
        var dst = {}
            ,src
            ,p
            ,args = [].splice.call(arguments, 0)
        ;
    
        while (args.length > 0) {
            src = args.splice(0, 1)[0];
            if (toString.call(src) == '[object Object]') {
                for (p in src) {
                    if (src.hasOwnProperty(p)) {
                        if (toString.call(src[p]) == '[object Object]') {
                            dst[p] = merge(dst[p] || {}, src[p]);
                        } else {
                            dst[p] = src[p];
                        }
                    }
                }
            }
        }
    
       return dst;
    }

例:

a = {
    "p1": "p1a",
    "p2": [
        "a",
        "b",
        "c"
    ],
    "p3": true,
    "p5": null,
    "p6": {
        "p61": "p61a",
        "p62": "p62a",
        "p63": [
            "aa",
            "bb",
            "cc"
        ],
        "p64": {
            "p641": "p641a"
        }
    }
};

b = {
    "p1": "p1b",
    "p2": [
        "d",
        "e",
        "f"
    ],
    "p3": false,
    "p4": true,
    "p6": {
        "p61": "p61b",
        "p64": {
            "p642": "p642b"
        }
    }
};

c = {
    "p1": "p1c",
    "p3": null,
    "p6": {
        "p62": "p62c",
        "p64": {
            "p643": "p641c"
        }
    }
};

d = merge(a, b, c);


/*
    d = {
        "p1": "p1c",
        "p2": [
            "d",
            "e",
            "f"
        ],
        "p3": null,
        "p5": null,
        "p6": {
            "p61": "p61b",
            "p62": "p62c",
            "p63": [
                "aa",
                "bb",
                "cc"
            ],
            "p64": {
                "p641": "p641a",
                "p642": "p642b",
                "p643": "p641c"
            }
        },
        "p4": true
    };
*/

与我在此页面上测试的其他函数相比,此函数是真正的递归(进行深度合并),并且模仿jQuery.extend()的功能。但是,最好修改第一个对象/参数,以便用户可以决定是否要传入空对象{}作为第一个参数,或者让函数修改原始对象。因此,如果你改变dst = {}dst = arguments[0]它会改变你的合并对象传递的第一个对象
Jasdeep卡尔萨

3
现在,它已停止在chrome中工作。我认为使用这种构造toString.call(src) == '[object Object]'来检查是否存在对象是个坏主意。typeof src好多了。此外,它将数组转换为对象(至少我在最新的chrome上有这种行为)。
m03geek 2015年

我需要在一个贪婪的循环中执行此操作,我已经将此函数对标了VS到目前为止我从lodash(某种下划线的lib)使用_.merge(object,[sources])的函数:您的函数快了将近两倍谢了哥们。
Arnaud Bouchot '16

20

Object.assign()

ECMAScript 2015(ES6)

这是一项新技术,是ECMAScript 2015(ES6)标准的一部分。该技术的规范已经完成,但是请检查兼容性表以了解各种浏览器的使用和实现状态。

Object.assign()方法用于将所有可枚举的自身属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };

var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1);  // { a: 1, b: 2, c: 3 }, target object itself is changed.

19

对于不太复杂的对象,可以使用JSON

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog', car: 'chevy'}
var objMerge;

objMerge = JSON.stringify(obj1) + JSON.stringify(obj2);

// {"food": "pizza","car":"ford"}{"animal":"dog","car":"chevy"}

objMerge = objMerge.replace(/\}\{/, ","); //  \_ replace with comma for valid JSON

objMerge = JSON.parse(objMerge); // { food: 'pizza', animal: 'dog', car: 'chevy'}
// Of same keys in both objects, the last object's value is retained_/

请注意,在此示例中,“} {” 一定不能出现在字符串中!


4
var so1 = JSON.stringify(obj1); var so2 = JSON.stringify(obj1); objMerge = so1.substr(0, so1.length-1)+","+so2.substr(1); console.info(objMerge);
Quamis 2013年

function merge(obj1, obj2) { return JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)) .replace(/\}\{/g, ',').replace(/,\}/g, '}').replace(/\{,/g, '{')); }
Alex Ivasyuv 2015年

还是objMerge = JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)).replace(/\}\{/, ", "));
单线

1
@Charles,对不起,但是“此方法非常危险”又如何有趣-这是最安全的方法第一,它使用JSON来完成读取对象内容的繁重工作,其次,它使用JSON对象,这绝对是最安全的机器经过优化,可以从JSON兼容的对象字符串文字中解析JSO。而且还向后兼容IE8,后者是第一个实现其标准的浏览器。这简直让我感到奇怪:“为什么你为什么要说这么愚蠢的话而逃之get”?
Bekim Bacaj

我相信由于无法使用stringify,因此该对象中的函数将在此处分解,但是我们可以澄清其他缺点吗?
yeahdixon

18

有一个叫库deepmergeGitHub上:这似乎是得到一些牵引。它是独立的,可通过npm和bower软件包管理器使用。

我倾向于对此进行使用或改进,而不是从答案中粘贴粘贴代码。


17

做到这一点的最好方法是使用Object.defineProperty添加一个不可枚举的适当属性。

这样,您仍然可以遍历对象的属性,而不必使用使用Object.prototype.extend创建属性时将获得的新创建的“ extend”。

希望这会有所帮助:

Object.defineProperty(Object.prototype,“ extend”,{
    枚举:错误,
    值:功能(来自){
        var props = Object.getOwnPropertyNames(from);
        var dest = this;
        props.forEach(function(name){
            如果(目标名称){
                var destination = Object.getOwnPropertyDescriptor(from,name);
                Object.defineProperty(目的地,名称,目的地);
            }
        });
        返回这个
    }
});

工作完成后,您可以执行以下操作:

var obj = {
    名称:“ stack”,
    完成:“溢出”
}
var替换= {
    名称:“ stock”
};

obj.extend(replacement);

我刚刚在这里写了一篇博客文章:http : //onemoredigit.com/post/1527191998/extending-objects-in-node-js


请稍等---代码不起作用!:(将其插入jsperf以便与其他合并算法进行比较,并且代码失败-函数扩展不存在
Jens Roland 2010年

16

您可以简单地使用jQuery extend

var obj1 = { val1: false, limit: 5, name: "foo" };
var obj2 = { val2: true, name: "bar" };

jQuery.extend(obj1, obj2);

现在obj1包含的所有值obj1obj2


2
obj1将包含所有值,而不是obj2。您正在扩展第一个对象。jQuery.extend( target [, object1 ] [, objectN ] )
ruuter 2015年

5
这个问题与jQuery无关。 JavaScript!= jQuery
Jorge Riv 2016年

1
我通过Google搜索了JavaScript,但这是一种更简单的方法
Ehsan

13

原型具有:

Object.extend = function(destination,source) {
    for (var property in source)
        destination[property] = source[property];
    return destination;
}

obj1.extend(obj2) 会做你想要的。


6
您是说Object.prototype.extend吗?无论如何,扩展Object.prototype并不是一个好主意。-1。
解决方案

想一想,它可能应该是Object.prototype,不是应该Object...好主意,这就是prototype.js框架的作用,因此,如果您正在使用它或依赖于它的另一个框架,Object.prototype将会对其进行扩展extend
短暂

2
我不是要竖起你,而是说没关系,因为prototype.js确实是一个糟糕的论点。Prototype.js很流行,但这并不意味着它们是JavaScript的榜样。查看JS专业人士对此原型库的详细评论。dhtmlkitchen.com/?category=/JavaScript/&date=2008/06/17/…–
SolutionYogi

7
谁在乎它的对与错?如果有效,那么有效。除了寻求保持合同,赢得新客户或按时完成任务外,等待完美解决方案的等待非常棒。给任何热衷于免费编程的人,他们最终将以“正确”的方式完成所有工作。
大卫,2009年

1
你们在哪里找到的Object.prototypeObject.extend不会干扰您的对象,而只是将函数附加到Object对象上。而且obj1.extend(obj2)是不正确的方式来火功能,使用Object.extend(obj1, obj2)insted的
artnikpro

13

**使用Object.assign或使用散布...运算符可以轻松合并对象**

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog', car: 'BMW' }
var obj3 = {a: "A"}


var mergedObj = Object.assign(obj1,obj2,obj3)
 // or using the Spread operator (...)
var mergedObj = {...obj1,...obj2,...obj3}

console.log(mergedObj);

对象从右到左合并,这意味着将覆盖与右边的对象具有相同属性的对象。

在此示例中,obj2.car重写obj1.car


12

就算有人使用Google Closure库

goog.require('goog.object');
var a = {'a': 1, 'b': 2};
var b = {'b': 3, 'c': 4};
goog.object.extend(a, b);
// Now object a == {'a': 1, 'b': 3, 'c': 4};

数组存在类似的辅助函数

var a = [1, 2];
var b = [3, 4];
goog.array.extend(a, b); // Extends array 'a'
goog.array.concat(a, b); // Returns concatenation of array 'a' and 'b'

10

我扩展了David Coallier的方法:

  • 增加了合并多个对象的可能性
  • 支持深层物体
  • 覆盖参数(如果最后一个参数是布尔值则检测到)

如果override为false,则不会覆盖任何属性,但会添加新的属性。

用法:obj.merge(合并... [,覆盖]);

这是我的代码:

Object.defineProperty(Object.prototype, "merge", {
    enumerable: false,
    value: function () {
        var override = true,
            dest = this,
            len = arguments.length,
            props, merge, i, from;

        if (typeof(arguments[arguments.length - 1]) === "boolean") {
            override = arguments[arguments.length - 1];
            len = arguments.length - 1;
        }

        for (i = 0; i < len; i++) {
            from = arguments[i];
            if (from != null) {
                Object.getOwnPropertyNames(from).forEach(function (name) {
                    var descriptor;

                    // nesting
                    if ((typeof(dest[name]) === "object" || typeof(dest[name]) === "undefined")
                            && typeof(from[name]) === "object") {

                        // ensure proper types (Array rsp Object)
                        if (typeof(dest[name]) === "undefined") {
                            dest[name] = Array.isArray(from[name]) ? [] : {};
                        }
                        if (override) {
                            if (!Array.isArray(dest[name]) && Array.isArray(from[name])) {
                                dest[name] = [];
                            }
                            else if (Array.isArray(dest[name]) && !Array.isArray(from[name])) {
                                dest[name] = {};
                            }
                        }
                        dest[name].merge(from[name], override);
                    } 

                    // flat properties
                    else if ((name in dest && override) || !(name in dest)) {
                        descriptor = Object.getOwnPropertyDescriptor(from, name);
                        if (descriptor.configurable) {
                            Object.defineProperty(dest, name, descriptor);
                        }
                    }
                });
            }
        }
        return this;
    }
});

示例和测试案例:

function clone (obj) {
    return JSON.parse(JSON.stringify(obj));
}
var obj = {
    name : "trick",
    value : "value"
};

var mergeObj = {
    name : "truck",
    value2 : "value2"
};

var mergeObj2 = {
    name : "track",
    value : "mergeObj2",
    value2 : "value2-mergeObj2",
    value3 : "value3"
};

assertTrue("Standard", clone(obj).merge(mergeObj).equals({
    name : "truck",
    value : "value",
    value2 : "value2"
}));

assertTrue("Standard no Override", clone(obj).merge(mergeObj, false).equals({
    name : "trick",
    value : "value",
    value2 : "value2"
}));

assertTrue("Multiple", clone(obj).merge(mergeObj, mergeObj2).equals({
    name : "track",
    value : "mergeObj2",
    value2 : "value2-mergeObj2",
    value3 : "value3"
}));

assertTrue("Multiple no Override", clone(obj).merge(mergeObj, mergeObj2, false).equals({
    name : "trick",
    value : "value",
    value2 : "value2",
    value3 : "value3"
}));

var deep = {
    first : {
        name : "trick",
        val : "value"
    },
    second : {
        foo : "bar"
    }
};

var deepMerge = {
    first : {
        name : "track",
        anotherVal : "wohoo"
    },
    second : {
        foo : "baz",
        bar : "bam"
    },
    v : "on first layer"
};

assertTrue("Deep merges", clone(deep).merge(deepMerge).equals({
    first : {
        name : "track",
        val : "value",
        anotherVal : "wohoo"
    },
    second : {
        foo : "baz",
        bar : "bam"
    },
    v : "on first layer"
}));

assertTrue("Deep merges no override", clone(deep).merge(deepMerge, false).equals({
    first : {
        name : "trick",
        val : "value",
        anotherVal : "wohoo"
    },
    second : {
        foo : "bar",
        bar : "bam"
    },
    v : "on first layer"
}));

var obj1 = {a: 1, b: "hello"};
obj1.merge({c: 3});
assertTrue(obj1.equals({a: 1, b: "hello", c: 3}));

obj1.merge({a: 2, b: "mom", d: "new property"}, false);
assertTrue(obj1.equals({a: 1, b: "hello", c: 3, d: "new property"}));

var obj2 = {};
obj2.merge({a: 1}, {b: 2}, {a: 3});
assertTrue(obj2.equals({a: 3, b: 2}));

var a = [];
var b = [1, [2, 3], 4];
a.merge(b);
assertEquals(1, a[0]);
assertEquals([2, 3], a[1]);
assertEquals(4, a[2]);


var o1 = {};
var o2 = {a: 1, b: {c: 2}};
var o3 = {d: 3};
o1.merge(o2, o3);
assertTrue(o1.equals({a: 1, b: {c: 2}, d: 3}));
o1.b.c = 99;
assertTrue(o2.equals({a: 1, b: {c: 2}}));

// checking types with arrays and objects
var bo;
a = [];
bo = [1, {0:2, 1:3}, 4];
b = [1, [2, 3], 4];

a.merge(b);
assertTrue("Array stays Array?", Array.isArray(a[1]));

a = [];
a.merge(bo);
assertTrue("Object stays Object?", !Array.isArray(a[1]));

a = [];
a.merge(b);
a.merge(bo);
assertTrue("Object overrides Array", !Array.isArray(a[1]));

a = [];
a.merge(b);
a.merge(bo, false);
assertTrue("Object does not override Array", Array.isArray(a[1]));

a = [];
a.merge(bo);
a.merge(b);
assertTrue("Array overrides Object", Array.isArray(a[1]));

a = [];
a.merge(bo);
a.merge(b, false);
assertTrue("Array does not override Object", !Array.isArray(a[1]));

我的equals方法可以在这里找到:JavaScript中的对象比较



9

Ext JS 4中,可以按照以下步骤进行操作:

var mergedObject = Ext.Object.merge(object1, object2)

// Or shorter:
var mergedObject2 = Ext.merge(object1, object2)

请参见merge(object):Object


1
提防EXT.Object.merge中的EXTJS-9173错误,至今仍未解决+2年
克里斯

9

哇..这是我见过的多页内容中的第一篇StackOverflow帖子。抱歉,添加另一个“答案”

此方法适用于ES5及更早版本 -还有很多其他答案可以解决ES6。

我没有看到利用该属性合并的任何“深层”对象arguments。这是我的答案- 紧凑递归,允许传递无限的对象参数:

function extend() {
    for (var o = {}, i = 0; i < arguments.length; i++) {
        // if (arguments[i].constructor !== Object) continue;
        for (var k in arguments[i]) {
            if (arguments[i].hasOwnProperty(k)) {
                o[k] = arguments[i][k].constructor === Object ? extend(o[k] || {}, arguments[i][k]) : arguments[i][k];
            }
        }
    }
    return o;
}

被注释掉的部分是可选的..它只会跳过传递的不是对象的参数(防止错误)。

例:

extend({
    api: 1,
    params: {
        query: 'hello'
    }
}, {
    params: {
        query: 'there'
    }
});

// outputs {api: 1, params: {query: 'there'}}

现在这个答案不过是沧海一粟...


最有效的最短答案!它值得更多的投票!
JensTörnell

8
var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }

// result
result: {food: "pizza", car: "ford", animal: "dog"}

使用jQuery.extend() - 链接

// Merge obj1 & obj2 to result
var result1 = $.extend( {}, obj1, obj2 );

使用_.merge() - 链接

// Merge obj1 & obj2 to result
var result2 = _.merge( {}, obj1, obj2 );

使用_.extend() - 链接

// Merge obj1 & obj2 to result
var result3 = _.extend( {}, obj1, obj2 );

使用Object.assign()ECMAScript 2015(ES6) - 链接

// Merge obj1 & obj2 to result
var result4 = Object.assign( {}, obj1, obj2 );

全部输出

obj1: { animal: 'dog' }
obj2: { food: 'pizza', car: 'ford' }
result1: {food: "pizza", car: "ford", animal: "dog"}
result2: {food: "pizza", car: "ford", animal: "dog"}
result3: {food: "pizza", car: "ford", animal: "dog"}
result4: {food: "pizza", car: "ford", animal: "dog"}

7

基于Markusvsync的答案,这是扩展版本。该函数接受任意数量的参数。它可用于在DOM节点上设置属性并制作值的深层副本。但是,第一个参数是通过引用给出的。

要检测DOM节点,请使用isDOMNode()函数(请参阅堆栈溢出问题JavaScript isDOM —如何检查JavaScript对象是否为DOM对象?

它已在Opera 11,Firefox 6,Internet Explorer 8和Google Chrome 16中进行了测试。

function mergeRecursive() {

  // _mergeRecursive does the actual job with two arguments.
  var _mergeRecursive = function (dst, src) {
    if (isDOMNode(src) || typeof src !== 'object' || src === null) {
      return dst;
    }

    for (var p in src) {
      if (!src.hasOwnProperty(p))
        continue;
      if (src[p] === undefined)
        continue;
      if ( typeof src[p] !== 'object' || src[p] === null) {
        dst[p] = src[p];
      } else if (typeof dst[p]!=='object' || dst[p] === null) {
        dst[p] = _mergeRecursive(src[p].constructor===Array ? [] : {}, src[p]);
      } else {
        _mergeRecursive(dst[p], src[p]);
      }
    }
    return dst;
  }

  // Loop through arguments and merge them into the first argument.
  var out = arguments[0];
  if (typeof out !== 'object' || out === null)
    return out;
  for (var i = 1, il = arguments.length; i < il; i++) {
    _mergeRecursive(out, arguments[i]);
  }
  return out;
}

一些例子

设置innerHTML和HTML元素的样式

mergeRecursive(
  document.getElementById('mydiv'),
  {style: {border: '5px solid green', color: 'red'}},
  {innerHTML: 'Hello world!'});

合并数组和对象。请注意,未定义可用于保留左侧数组/对象中的值。

o = mergeRecursive({a:'a'}, [1,2,3], [undefined, null, [30,31]], {a:undefined, b:'b'});
// o = {0:1, 1:null, 2:[30,31], a:'a', b:'b'}

任何不适合JavaScript对象的参数(包括null)都将被忽略。除了第一个参数外,还将删除DOM节点。注意,像new String()这样创建的字符串实际上是对象。

o = mergeRecursive({a:'a'}, 1, true, null, undefined, [1,2,3], 'bc', new String('de'));
// o = {0:'d', 1:'e', 2:3, a:'a'}

如果要将两个对象合并到一个新的对象中(不影响两个对象中的任何一个),请提供{}作为第一个参数

var a={}, b={b:'abc'}, c={c:'cde'}, o;
o = mergeRecursive(a, b, c);
// o===a is true, o===b is false, o===c is false

编辑(通过ReaperSoon):

还合并数组

function mergeRecursive(obj1, obj2) {
  if (Array.isArray(obj2)) { return obj1.concat(obj2); }
  for (var p in obj2) {
    try {
      // Property in destination object set; update its value.
      if ( obj2[p].constructor==Object ) {
        obj1[p] = mergeRecursive(obj1[p], obj2[p]);
      } else if (Array.isArray(obj2[p])) {
        obj1[p] = obj1[p].concat(obj2[p]);
      } else {
        obj1[p] = obj2[p];
      }
    } catch(e) {
      // Property in destination object not set; create it and set its value.
      obj1[p] = obj2[p];
    }
  }
  return obj1;
}


5

您应该使用lodash的默认值

_.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } });
// → { 'user': { 'name': 'barney', 'age': 36 } }

5

使用Underscore.js,可以合并对象数组:

var arrayOfObjects = [ {a:1}, {b:2, c:3}, {d:4} ];
_(arrayOfObjects).reduce(function(memo, o) { return _(memo).extend(o); });

结果是:

Object {a: 1, b: 2, c: 3, d: 4}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.