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

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

obj1.merge(obj2);

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

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


当前回答

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

其他回答

此解决方案创建一个新对象,并能够处理多个对象。

此外,它是递归的,您可以选择要覆盖值和对象的天气。

    function extendObjects() {

        var newObject        = {};
        var overwriteValues  = false;
        var overwriteObjects = false;

        for ( var indexArgument = 0; indexArgument < arguments.length; indexArgument++ ) {

            if ( typeof arguments[indexArgument] !== 'object' ) {

                if ( arguments[indexArgument] == 'overwriteValues_True' ) {

                    overwriteValues = true;            
                } else if ( arguments[indexArgument] == 'overwriteValues_False' ) {

                    overwriteValues = false;                             
                } else if ( arguments[indexArgument] == 'overwriteObjects_True' ) {

                    overwriteObjects = true;     
                } else if ( arguments[indexArgument] == 'overwriteObjects_False' ) {

                    overwriteObjects = false; 
                }

            } else {

                extendObject( arguments[indexArgument], newObject, overwriteValues, overwriteObjects );
            }

        }

        function extendObject( object, extendedObject, overwriteValues, overwriteObjects ) {

            for ( var indexObject in object ) {

                if ( typeof object[indexObject] === 'object' ) {

                    if ( typeof extendedObject[indexObject] === "undefined" || overwriteObjects ) {
                        extendedObject[indexObject] = object[indexObject];
                    }

                    extendObject( object[indexObject], extendedObject[indexObject], overwriteValues, overwriteObjects );

                } else {

                    if ( typeof extendedObject[indexObject] === "undefined" || overwriteValues ) {
                        extendedObject[indexObject] = object[indexObject];
                    }

                }

            }     

            return extendedObject;

        }

        return newObject;
    }

    var object1           = { a : 1, b : 2, testArr : [888, { innArr : 1 }, 777 ], data : { e : 12, c : { lol : 1 }, rofl : { O : 3 } } };
    var object2           = { a : 6, b : 9, data : { a : 17, b : 18, e : 13, rofl : { O : 99, copter : { mao : 1 } } }, hexa : { tetra : 66 } };
    var object3           = { f : 13, g : 666, a : 333, data : { c : { xD : 45 } }, testArr : [888, { innArr : 3 }, 555 ]  };

    var newExtendedObject = extendObjects( 'overwriteValues_False', 'overwriteObjects_False', object1, object2, object3 );

newExtendedObject的内容:

{"a":1,"b":2,"testArr":[888,{"innArr":1},777],"data":{"e":12,"c":{"lol":1,"xD":45},"rofl":{"O":3,"copter":{"mao":1}},"a":17,"b":18},"hexa":{"tetra":66},"f":13,"g":666}

小提琴:http://jsfiddle.net/o0gb2umb/

在一行代码中合并N个对象的财产

Object.assign方法是ECMAScript 2015(ES6)标准的一部分,它完全符合您的需要。(不支持IE)

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

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

阅读更多。。。

支持旧浏览器的polyfill:

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.prototype.merge = function(obj, override) {
// Don't override by default

    for (var key in obj) {
        var n = obj[key];
        var t = this[key];
        this[key] = (override && t) ? n : t;
    };

};

测试数据如下:

var Mammal = function () {
    this.eyes = 2;
    this.thinking_brain = false;
    this.say = function () {
    console.log('screaming like a mammal')};
}

var Human = function () {
    this.thinking_brain = true;
    this.say = function() {console.log('shouting like a human')};
}

john = new Human();

// Extend mammal, but do not override from mammal
john.merge(new Mammal());
john.say();

// Extend mammal and override from mammal
john.merge(new Mammal(), true);
john.say();

浅的

var obj = { name : "Jacob" , address : ["America"] }
var obj2 = { name : "Shaun" , address : ["Honk Kong"] }

var merged = Object.assign({} , obj,obj2 ); //shallow merge 
obj2.address[0] = "new city"

result.地址[0]更改为“新城”,即合并对象也更改。这就是浅层合并的问题。

deep

var obj = { name : "Jacob" , address : ["America"] }
var obj2 = { name : "Shaun" , address : ["Honk Kong"] }

var result = Object.assign({} , JSON.parse(JSON.stringify(obj)),JSON.parse(JSON.stringify(obj2)) )

obj2.address[0] = "new city"

result.address[0]未更改

gossi对David Coallier方法的扩展:

检查这两行:

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

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

“from”不是有效对象

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