Come posso unire le proprietà di due oggetti JavaScript in modo dinamico?

Devo essere in grado di unire due (molto semplici) oggetti JavaScript in fase di esecuzione. Ad esempio mi piacerebbe:

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

Qualcuno ha uno script per questo o sa di un modo per farlo? Non ho bisogno di ricorsione, e non ho bisogno di unire funzioni, solo metodi su oggetti piatti.

Metodo standard ECMAScript 2018

Usi la diffusione di oggetti :

 let merged = {...obj1, ...obj2}; /** 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}; 

Metodo standard 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); 

(vedi riferimento JavaScript MDN )


Metodo per ES5 e precedenti

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

Si noti che questo aggiungerà semplicemente tutti gli attributi di obj2 a obj1 che potrebbero non essere ciò che si desidera se si desidera comunque utilizzare l’object non modificato obj1 .

Se stai usando un framework che fa schifo su tutti i tuoi prototipi, allora devi diventare più esperto con controlli come hasOwnProperty , ma quel codice funzionerà per il 99% dei casi.

Funzione di esempio:

 /** * 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 ha anche un’utilità per questo: http://api.jquery.com/jQuery.extend/ .

Tratto dalla documentazione di 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" } 

Il codice precedente muterà l’ object esistente denominato settings .


Se vuoi creare un nuovo object senza modificare alcun argomento, usa questo:

 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) specifica Object.assign che lo farà.

 Object.assign(obj1, obj2); 

L’attuale supporto per i browser sta migliorando , ma se stai sviluppando per i browser che non dispongono di supporto, puoi utilizzare un polyfill .

Ho cercato su google il codice per unire le proprietà dell’object e sono finito qui. Tuttavia, poiché non c’era alcun codice per la fusione ricorsiva, l’ho scritto io stesso. (Forse jQuery estende è ricorsivo BTW?) Comunque, si spera che qualcun altro lo troverà utile pure.

(Ora il codice non usa Object.prototype 🙂

Codice

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

Un esempio

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

Produce object o3 come

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

Nota che il metodo di extend underscore.js lo fa in un unico elemento :

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

Simile a jQuery extend (), hai la stessa funzione in 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); 

Ho bisogno di unire gli oggetti oggi e questa domanda (e le risposte) mi hanno aiutato molto. Ho provato alcune risposte, ma nessuna corrisponde alle mie esigenze, quindi ho unito alcune delle risposte, ho aggiunto qualcosa di me stesso e ho creato una nuova funzione di unione. Ecco qui:

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

Alcuni esempi di utilizzo:

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

È ansible utilizzare le proprietà di diffusione degli oggetti, attualmente una proposta ECMAScript di fase 3.

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

Le soluzioni date dovrebbero essere modificate per verificare source.hasOwnProperty(property) nei cicli for..in prima di assegnarle, altrimenti si finisce per copiare le proprietà dell’intera catena di prototipi, che è raramente desiderata …

Unisci le proprietà di N oggetti in una riga di codice

Un metodo Object.assign fa parte dello standard ECMAScript 2015 (ES6) e fa esattamente ciò di cui hai bisogno. ( IE non supportato)

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

Il metodo Object.assign () viene utilizzato per copiare i valori di tutte le proprietà enumerabili di uno o più oggetti di origine in un object di destinazione.

Leggi di più…

Il polyfill per supportare i browser più vecchi:

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

I seguenti due sono probabilmente un buon punto di partenza. lodash ha anche una funzione di personalizzazione per quei bisogni speciali!

_.extend ( http://underscorejs.org/#extend )
_.merge ( https://lodash.com/docs#merge )

A proposito, tutto quello che stai facendo è sovrascrivere le proprietà, non unire …

Questo è il modo in cui l’area di oggetti JavaScript è veramente unita: solo le chiavi nell’object to che non sono oggetti saranno sovrascritte da. Tutto il resto sarà veramente unito . Ovviamente puoi cambiare questo comportamento per non sovrascrivere tutto ciò che esiste come se solo to[n] is undefined , ecc …:

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

Uso:

 var merged = realMerge(obj1, obj2); 

Ecco la mia pugnalata che

  1. Supporta l’unione profonda
  2. Non muta gli argomenti
  3. Prende un numero qualsiasi di argomenti
  4. Non estende il prototipo dell’object
  5. Non dipende da un’altra libreria ( jQuery , MooTools , Underscore.js , ecc.)
  6. Include il controllo di hasOwnProperty
  7. È corto 🙂

     /* Recursively merge properties and return new object obj1 <- obj2 [ <- ... ] */ 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; } 

Esempio:

 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.assign ()

ECMAScript 2015 (ES6)

Questa è una nuova tecnologia, parte dello standard ECMAScript 2015 (ES6). La specifica di questa tecnologia è stata finalizzata, ma controlla la tabella di compatibilità per l’utilizzo e lo stato di implementazione in vari browser.

Il metodo Object.assign () viene utilizzato per copiare i valori di tutte le proprietà enumerabili di uno o più oggetti di origine in un object di destinazione. Restituirà l’object target.

 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. 

Per oggetti non troppo complicati potresti usare 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_/ 

Tenete presente che in questo esempio “} {” non deve verificarsi all’interno di una stringa!

Il modo migliore per farlo è aggiungere una proprietà corretta che non è enumerabile utilizzando Object.defineProperty.

In questo modo sarai ancora in grado di scorrere le proprietà degli oggetti senza avere la “estensione” appena creata che avresti ottenuto se dovessi creare la proprietà con Object.prototype.extend.

Speriamo che questo aiuti:

 Object.defineProperty (Object.prototype, "extend", {
     enumerabile: falso,
     valore: function (from) {
         var props = Object.getOwnPropertyNames (da);
         var dest = this;
         props.forEach (function (name) {
             if (nome in dest) {
                 var destination = Object.getOwnPropertyDescriptor (from, name);
                 Object.defineProperty (dest, name, destination);
             }
         });
         restituiscilo;
     }
 });

Una volta che hai funzionato, puoi fare:

 var obj = {
     nome: 'stack',
     finitura: 'overflow'
 }
 var replacement = {
     nome: 'magazzino'
 };

 obj.extend (sostituzione);

Ho appena scritto un post sul blog qui: http://onemoredigit.com/post/1527191998/extending-objects-in-node-js

Puoi semplicemente usare extend jQuery

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

Ora obj1 contiene tutti i valori di obj1 e obj2

C’è una libreria chiamata deepmerge su GitHub : sembra avere una certa trazione. È uno standalone, disponibile attraverso i gestori di pacchetti npm e bower.

Sarei propenso a usare o migliorare su questo invece del codice di copia-incolla dalle risposte.

Solo se qualcuno sta utilizzando 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}; 

Esiste una funzione di supporto simile per l’array :

 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' 

Il prototipo ha questo:

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

obj1.extend(obj2) farà ciò che vuoi.

In MooTools , c’è Object.merge () :

 Object.merge(obj1, obj2); 

Ho esteso il metodo di David Coallier:

  • Aggiunta la possibilità di unire più oggetti
  • Supporta oggetti profondi
  • override parametro (che viene rilevato se l’ultimo parametro è un booleano)

Se override è false, nessuna proprietà viene ignorata ma verranno aggiunte nuove proprietà.

Uso: obj.merge (unisce … [, sovrascrivi]);

Ecco il mio codice:

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

Esempi e testcase:

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

My equals method can be found here: Object comparison in JavaScript

In Ext JS 4 it can be done as follows:

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

See merge( object ) : Object .

Merging objects is simple.

 var obj1 = { food: 'pizza', car: 'ford' } var obj2 = { animal: 'dog', car: 'BMW' } var obj3 = {a: "A"} var mergedObj = Object.assign(obj1,obj2,obj3) console.log(mergedObj); 

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

It results in:

 Object {a: 1, b: 2, c: 3, d: 4} 

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.

Codice

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

Qualche esempio

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

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:

Codice:

 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 .

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

🙂