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

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

obj1.merge(obj2);

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

是否有一种内置的方法来实现这一点?我不需要递归,也不需要合并函数,只需要平面对象上的方法。


当前回答

gossi对David Coallier方法的扩展:

检查这两行:

from = arguments[i];
Object.getOwnPropertyNames(from).forEach(function (name) {

需要针对空对象检查“from”。。。例如,如果合并以前在服务器上创建的来自Ajax响应的对象,则对象属性的值可以为“null”,在这种情况下,上述代码会生成一个错误消息:

“from”不是有效对象

因此,例如,将“…Object.getOwnPropertyNames(from).forEach…”函数包装为“if(from!=null){…}”将防止发生该错误。

其他回答

我的方式:

function mergeObjects(defaults, settings) {
    Object.keys(defaults).forEach(function(key_default) {
        if (typeof settings[key_default] == "undefined") {
            settings[key_default] = defaults[key_default];
        } else if (isObject(defaults[key_default]) && isObject(settings[key_default])) {
            mergeObjects(defaults[key_default], settings[key_default]);
        }
    });

    function isObject(object) {
        return Object.prototype.toString.call(object) === '[object Object]';
    }

    return settings;
}

:)

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提议对象rest spread插件才能工作(该插件包含在ES2018中的@babel/preset-env中)。

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

我在谷歌上搜索了合并对象财产的代码,结果出现在这里。然而,由于没有递归合并的代码,我自己编写了它。(也许jQuery扩展是递归的BTW?)无论如何,希望其他人也会发现它很有用。

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

Code

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

使用jQuery库尝试这种方式

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

console.log(jQuery.extend(obj1, obj2))

JSON兼容JavaScript对象的合并

我鼓励使用和利用不修改原始源代码的非破坏性方法,“Object.assign”是一种破坏性的方法,而且它也不太适合生产,因为它在早期浏览器上停止工作,而且你没有办法用其他方法对它进行干净的修补。

无论采用何种解决方案,合并JS对象都将永远无法实现或不完整。但是,合并JSON兼容的兼容对象离能够编写一段简单且可移植的代码只有一步之遥。该代码是一种非破坏性方法,将一系列JS对象合并到一个返回的主对象中,该主对象包含所有唯一的属性名及其对应的值,这些属性名合成在一个主对象中用于预期目的。

考虑到MSIE8是第一个为JSON对象添加本地支持的浏览器,这是一种极大的解脱,重用现有技术始终是一个受欢迎的机会。

将代码限制为JSON兼容的标准对象,与其说是限制,不如说是优势,因为这些对象也可以通过互联网传输。当然,对于那些想要更深入的向后兼容性的人来说,总是有一个json插件。,其方法可以很容易地分配给外部代码中的JSON变量,而无需修改或重写正在使用的方法。

function Merge( ){
    var a = [].slice.call( arguments ), i = 0;
        while( a[i] )a[i] = JSON.stringify( a[i++] ).slice( 1,-1 );
        return JSON.parse( "{"+ a.join() +"}" );
    }

(当然,人们总是可以给它取一个更有意义的名字,我还没有决定;应该把它命名为JSONmerge)

用例:

var master = Merge( obj1, obj2, obj3, ...objn );

现在,与Object.assign相反,这将使所有对象保持不变,并保持其原始状态(以防您做错了什么,需要重新排序合并对象,或者在再次合并之前能够将它们单独用于其他操作)。

Merge参数的数量也仅受参数长度限制[这是巨大的]的限制。本机支持的JSON parse/stringify已经进行了机器优化,这意味着:它应该比任何脚本形式的JS循环都快。对给定参数的迭代是使用while完成的,while被证明是JS中最快的循环。

简单地提一下我们已经知道的事实,即唯一对象标签(键)的重复财产将被包含相同键标签的后一个对象覆盖,这并不有害,这意味着您可以通过简单地对参数列表进行排序或重新排序来控制哪个属性将取代前一个属性。以及获得一个干净且更新的主对象而无重复作为最终输出的好处。

;var obj1={a:1},obj2={b:2},obj3={c:3};函数合并(){var a=[].slice.call(参数),i=0;而(a[i])a[i]=JSON.stringify(a[i++]).slice(1,-1);return JSON.parse(“{”+a.join()+“}”);};var master=合并(obj1、obj2、obj3);console.log(JSON.stringify(master));