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

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

var obj1 = { food: 'pizza', car: 'ford' } var obj2 = { animal: 'dog' } obj1.merge(obj2); //obj1 now has three properties: food, car, and animal 

有没有人有这个脚本或知道内置的方式来做到这一点? 我不需要recursion,我不需要合并函数,只需要平面对象上的方法。

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%的情况下工作。

function示例:

 /** * 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也有一个这样的工具: 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的对象。

如果你想创build一个新的对象而不修改任何参数,使用这个:

 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. 

Harmony ECMAScript 2015(ES6)指定了Object.assign ,它将执行此操作。

 Object.assign(obj1, obj2); 

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

我google代码来合并对象属性,并在这里结束。 但是因为没有任何代码recursion合并我自己写了。 (也许jQuery扩展是recursionBTW?)无论如何,希望别人也会觉得它有用。

(现在的代码不使用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 } } }; 

请注意, underscore.jsextend方法在一行中是这样做的:

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

和jQuery的extend()类似,你在AngularJS里有相同的function:

 // 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); 

我今天需要合并对象,这个问题(和答案)帮助了我很多。 我尝试了一些答案,但没有一个符合我的需求,所以我结合了一些答案,自己添加了一些东西,并提出了一个新的合并函数。 这里是:

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

应该修改给定的解决scheme,以便在分配之前检查for..in循环中的source.hasOwnProperty(property) – 否则,最终会复制整个原型链的属性,这是很less需要的。

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

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

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

Object.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; } }); } 

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

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

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

 const obj1 = { food: 'pizza', car: 'ford' }; const obj2 = { animal: 'dog' }; const obj3 = { ...obj1, ...obj2 }; console.log(obj3); 

顺便说一句,你所做的是覆盖属性,而不是合并…

这是JavaScript对象区域真正合并的方式:只有对象中不是对象本身的键才会被覆盖。 其他一切都将真正合并 。 当然,你可以改变这种行为,不覆盖任何存在的东西,只要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); 

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. 

对于不太复杂的对象,可以使用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_/ 

请注意,在这个例子中“} {” 不能出现在一个string中!

这是我的刺

  1. 支持深度合并
  2. 不改变参数
  3. 采取任何数量的论据
  4. 不扩展对象原型
  5. 不依赖于另一个库( jQuery , MooTools , Underscore.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 }; */ 

为此,最好的方法是使用Object.defineProperty添加一个不可枚举的属性。

这样,如果要用Object.prototype.extend创build属性,您仍然可以遍历对象属性,而不必创build新创build的“扩展”。

希望这有助于:

 Object.defineProperty(Object.prototype,“extend”,{
     enumerable:false,
     value:function(from){
         var props = Object.getOwnPropertyNames(from);
         var dest = this;
         props.forEach(function(name){
             if(name in dest){
                 var destination = Object.getOwnPropertyDescriptor(from,name);
                 Object.defineProperty(dest,name,destination);
             }
         });
        返回这个;
     }
 });

一旦你有这个工作,你可以做:

 var obj = {
    名称:'堆栈',
    完成:“溢出”
 }
 var replacement = {
    名称:'股票'
 };

 obj.extend(replace);

我刚刚在这里写了一篇博文: http :

你可以简单地使用jQuery的extend

 var obj1 = { val1: false, limit: 5, name: "foo" }; var obj2 = { val2: true, name: "bar" }; jQuery.extend(obj1, obj2); 

现在obj1包含obj1obj2所有值

只要有人在使用Google Closure Library :

 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' 

在GitHub上有一个名为deepmerge的库:这似乎正在获得一些牵引力。 这是一个独立的,可以通过npm和bower包pipe理器。

我会倾向于使用或改进,而不是从答案复制粘贴代码。

原型有这样的:

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

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

在MooTools中 ,有Object.merge() :

 Object.merge(obj1, obj2); 

我扩展了David Coallier的方法:

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

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

用法:obj.merge(merges … [,override]);

这是我的代码:

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

示例和testing用例:

 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.bc = 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方法可以在这里find: 在JavaScript中的对象比较

在Ext JS 4中可以这样做:

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

See merge( object ) : Object .

With Underscore.js , to merge an array of objects do:

 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} 

You should use lodash's defaultsDeep

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

It's worth mentioning that the version from the 140byt.es collection is solving the task within minimum space and is worth a try for this purpose:

码:

 function m(a,b,c){for(c in b)b.hasOwnProperty(c)&&((typeof a[c])[0]=='o'?m(a[c],b[c]):a[c]=b[c])} 

Usage for your purpose:

 m(obj1,obj2); 

Here's the original Gist .

Based on Markus' and vsync' answer , this is an expanded version. The function takes any number of arguments. It can be used to set properties on DOM nodes and makes deep copies of values. However, the first argument is given by reference.

To detect a DOM node, the isDOMNode() function is used (see Stack Overflow question JavaScript isDOM — How do you check if a JavaScript Object is a DOM Object? )

It was tested in Opera 11, Firefox 6, Internet Explorer 8 and 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; } 

一些例子

Set innerHTML and style of a HTML Element

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

Merge arrays and objects. Note that undefined can be used to preserv values in the lefthand array/object.

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

Any argument not beeing a JavaScript object (including null) will be ignored. Except for the first argument, also DOM nodes are discarded. Beware that ie strings, created like new String() are in fact objects.

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

If you want to merge two objects into a new (without affecting any of the two) supply {} as first argument

 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 

Edit (by ReaperSoon):

To also merge arrays

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

I use the following which is in pure JavaScript. It starts from the right-most argument and combines them all the way up to the first argument. There is no return value, only the first argument is modified and the left-most parameter (except the first one) has the highest weight on properties.

 var merge = function() { var il = arguments.length; for (var i = il - 1; i > 0; --i) { for (var key in arguments[i]) { if (arguments[i].hasOwnProperty(key)) { arguments[0][key] = arguments[i][key]; } } } }; 

My way:

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

🙂

You can merge objects through following my method

 var obj1 = { food: 'pizza', car: 'ford' }; var obj2 = { animal: 'dog' }; var result = mergeObjects([obj1, obj2]); console.log(result); document.write("result: <pre>" + JSON.stringify(result, 0, 3) + "</pre>"); function mergeObjects(objectArray) { if (objectArray.length) { var b = "", i = -1; while (objectArray[++i]) { var str = JSON.stringify(objectArray[i]); b += str.slice(1, str.length - 1); if (objectArray[i + 1]) b += ","; } return JSON.parse("{" + b + "}"); } return {}; }