Come posso unire dinamicamente le proprietà di due oggetti JavaScript?


2520

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

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 integrato per farlo? Non ho bisogno di ricorsione e non ho bisogno di unire funzioni, solo metodi su oggetti piatti.


Vale la pena notare questa risposta su una domanda simile , che mostra come unire "un livello inferiore". Cioè, unisce i valori delle chiavi duplicate (invece di sovrascrivere il primo valore con il secondo valore), ma non ricorre oltre. IMHO, è un buon codice pulito per quell'attività.
ToolmakerSteve

A proposito, le prime risposte fanno un'unione "superficiale": se la stessa chiave esiste sia in obj1 che in obj2, il valore in obj2 viene mantenuto, il valore in obj1 viene eliminato. Ad esempio, se l'esempio della domanda avesse var obj2 = { animal: 'dog', food: 'bone' };, l'unione sarebbe { food: 'bone', car: 'ford', animal: 'dog' }. Se stai lavorando con "dati nidificati" e desideri una "fusione profonda", cerca le risposte che menzionano "fusione profonda" o "ricorsione". Se hai valori che lo sono arrays, usa l'opzione "arrayMerge" di github "TehShrike / deepmerge", come menzionato qui .
ToolmakerSteve

Seguire il link seguente stackoverflow.com/questions/171251/… L'operatore di diffusione che è la nuova funzionalità della versione ES6
Dev216

Risposte:


2909

Metodo standard ECMAScript 2018

Dovresti usare la diffusione dell'oggetto :

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

mergedè ora l'unione di obj1e obj2. Le proprietà in obj2sovrascriveranno quelle in 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};

Ecco anche la documentazione MDN per questa sintassi. Se stai usando babel avrai bisogno del plug-in babel-plugin-transform-object-rest-spread affinché funzioni.

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 è sufficiente aggiungere tutti gli attributi di obj2a obj1che potrebbe non essere quello che vuoi se si vuole ancora utilizzare il non modificato obj1.

Se stai usando un framework che si diffonde su tutti i tuoi prototipi, devi diventare più elaborato con assegni simili hasOwnProperty, ma quel codice funzionerà per il 99% dei casi.

Esempio di funzione:

/**
 * 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
Questo non funziona se gli oggetti hanno attributi con lo stesso nome e si desidera unire gli attributi.
Xiè Jìléi,

69
Questo fa solo una copia / unione superficiale. Ha il potenziale per bloccare molti elementi.
Jay Taylor,

45
+1 per aver riconosciuto che alcune povere anime sono costrette a usare strutture che fanno schifo su tutti i loro prototipi ...
thejonwithnoh

4
Questo non ha funzionato per me a causa di "Pertanto assegna proprietà anziché semplicemente copiare o definire nuove proprietà. Ciò potrebbe renderlo inadatto per la fusione di nuove proprietà in un prototipo se le origini di unione contengono getter." ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ). Ho dovuto usare var merged = {...obj1, ...obj2}.
Morgler

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

1191

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 sopra cambierà l' oggetto esistente chiamato settings.


Se vuoi creare un nuovo oggetto senza modificare nessuno dei due argomenti, 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.

166
Attenzione: la variabile "impostazioni" verrà comunque modificata. jQuery non restituisce una nuova istanza. La ragione di questo (e per la denominazione) è che .extend () è stato sviluppato per estendere gli oggetti, piuttosto che per mescolare cose insieme. Se si desidera un nuovo oggetto (ad es. Impostazioni predefinite che non si desidera toccare), è sempre possibile jQuery.extend ({}, impostazioni, opzioni);
webmat

1
Bene! Grazie mille. Come affermato in precedenza, il nome è scarsamente chiamato. ho cercato i documenti jQuery indietro e quarta e non mi sono imbattuto in esso.
Mike Starov,

28
Intendiamoci, jQuery.extend ha anche un'impostazione profonda (booleana). jQuery.extend(true,settings,override), che è importante se una proprietà nelle impostazioni contiene un oggetto e la sostituzione ha solo una parte di tale oggetto. Invece di rimuovere le proprietà senza pari, l'impostazione profonda verrà aggiornata solo dove esiste. L'impostazione predefinita è falsa.
vol7ron,

19
Accidenti, hanno davvero fatto un buon lavoro nominandolo. Se cerchi come estendere un oggetto Javascript, arriva subito! Tuttavia, potresti non colpirti se stai cercando di fondere o ricucire oggetti Javascript.
Derek Greer,

2
Non dire alle persone di prendere in considerazione l'uso di un metodo di libreria quando un paio di linee di JS vaniglia faranno quello che vogliono. C'è stato un tempo prima di jQuery!
CrazyMerlin,

348

L' Harmony ECMAScript 2015 (ES6) specifica Object.assignche faranno questo.

Object.assign(obj1, obj2);

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


35
Si noti che questa è solo una fusione superficiale
Joachim Lous

Penso che nel frattempo Object.assignabbia una copertura abbastanza decente: kangax.github.io/compat-table/es6/…
LazerBass,

13
Questa dovrebbe ora essere la risposta corretta. Oggi le persone compilano il loro codice per essere compatibili con il raro browser (IE11) che non supporta questo tipo di cose. Nota a margine: se non vuoi aggiungere obj2 a obj1, puoi restituire un nuovo oggetto usandoObject.assign({}, obj1, obj2)
Duvrai,

6
@Duvrai Secondo i rapporti che ho visto, IE11 non è sicuramente raro a circa il 18% della quota di mercato a luglio 2016.
NanoWizard

3
@Kermani L'OP sottolinea specificamente che la ricorsione non è necessaria. Pertanto, mentre è utile sapere che questa soluzione esegue una fusione superficiale, questa risposta è sufficiente così com'è. Il commento di JoachimLou ha ricevuto meritevoli voti e fornisce tali informazioni extra quando necessario. Penso che la differenza tra fusione profonda e superficiale e argomenti simili esuli dallo scopo di questa discussione e si adatti meglio ad altre domande sulla SO.
NanoWizard,

264

Ho cercato su Google il codice per unire le proprietà degli oggetti e sono finito qui. Tuttavia, poiché non c'era alcun codice per l'unione ricorsiva, l'ho scritto da solo. (Forse jQuery extension è ricorsivo a proposito?) Comunque, si spera che anche qualcun altro lo trovi utile.

(Ora il codice non utilizza 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 oggetto o3 come

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

11
Bello, ma prima farei una copia profonda degli oggetti. In questo modo anche o1 verrebbe modificato, poiché gli oggetti vengono passati per riferimento.
skerit,

1
Questo era quello che stavo cercando. Assicurati di verificare se obj2.hasOwnProperty (p) prima di andare all'istruzione try catch. Altrimenti, potresti finire con qualche altra schifezza che si fonde dall'alto nella catena del prototipo.
Adam,

3
Grazie Markus. Si noti che anche o1 viene modificato da MergeRecursive, quindi alla fine, o1 e o3 sono identici. Potrebbe non essere più intuitivo se non si restituisce nulla, quindi l'utente sa che ciò che sta fornendo (o1) viene modificato e diventa il risultato. Inoltre, nel tuo esempio, potrebbe valere la pena aggiungere qualcosa in o2 che non è originariamente in o1, per mostrare che le proprietà di o2 vengono inserite in o1 (qualcun altro sotto il codice alternativo inviato che copia il tuo esempio, ma la loro si interrompe quando o2 contiene proprietà non in o1 - ma il tuo funziona in questo senso).
pancake

7
Questa è una buona risposta, ma un paio di cavilli: 1) Nessuna logica dovrebbe essere basata su eccezioni; in questo caso, qualsiasi eccezione generata verrà colta con risultati imprevedibili. 2) Concordo con il commento di @ pancake sul non restituire nulla, poiché l'oggetto originale viene modificato. Ecco un esempio di jsFiddle senza la logica dell'eccezione: jsfiddle.net/jlowery2663/z8at6knn/4 - Jeff Lowery
Jeff Lowery

3
Inoltre, obj2 [p] .constructor == Array è necessario in ogni caso in cui sia presente un array di oggetti.
Il compositore il

175

Nota che underscore.js's extend-method fa questo in un one-liner:

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

36
Questo esempio va bene se hai a che fare con oggetti anonimi, ma se non è così il commento di webmat nell'avviso di risposta jQuery sulle mutazioni si applica qui, poiché il carattere di sottolineatura muta anche l'oggetto di destinazione . Come la risposta di jQuery, per fare questo senza mutazione basta fondersi in un oggetto vuoto:_({}).extend(obj1, obj2);
Abe Voelker

8
Ce n'è un altro che potrebbe essere rilevante a seconda di ciò che stai cercando di ottenere: _.defaults(object, *defaults)"Compila proprietà null e indefinite nell'oggetto con valori dagli oggetti predefiniti e restituisci l'oggetto".
Conny il

Molte persone hanno commentato la mutazione dell'oggetto di destinazione, ma piuttosto che avere un metodo per crearne uno nuovo, uno schema comune (ad esempio in dojo) è dire dojo.mixin (dojo.clone (myobj), {newpropertys: "da aggiungere a myobj "})
Colin D,

7
Il carattere di sottolineatura può richiedere un numero arbitrario di oggetti, quindi puoi evitare di fare la mutazione dell'oggetto originale var mergedObj = _.extend({}, obj1, obj2).
lobati,

84

Simile a jQuery extender (), 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);

1
@naXa Penso che sarebbe un'opzione se non vuoi aggiungere una lib solo per questo fn.
juanmf,

67

Oggi devo unire gli oggetti e questa domanda (e risposte) mi ha aiutato molto. Ho provato alcune delle risposte, ma nessuna di queste si adattava alle mie esigenze, quindi ho combinato alcune delle risposte, aggiunto qualcosa da me e ho trovato 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 }));

3
buona risposta, ma penso che se una proprietà è presente nella prima e nella seconda e stai cercando di creare un nuovo oggetto fondendo prima e seconda, quelle uniche presenti nel primo oggetto andranno perse
Attaccanti

2
Ma non è un comportamento previsto? Lo scopo di questa funzione è sostituire i valori nell'ordine degli argomenti. Il prossimo sostituirà la corrente. Come puoi conservare valori univoci? Dal mio esempio, cosa ti aspetti merge({}, t1, { key1: 1 })?
Emre Erkan,

La mia aspettativa è quella di unire solo valori non tipizzati da oggetti.
AGamePlayer

fallisce se il risultato atteso è il seguente: gist.github.com/ceremcem/a54f90923c6e6ec42c7daf24cf2768bd
ceremcem

Questo ha funzionato per me in modalità rigorosa per il mio progetto node.js. Finora, questa è la migliore risposta a questo post.
Klewis,

48

È possibile utilizzare le proprietà di diffusione dell'oggetto, attualmente una proposta ECMAScript della fase 3.

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

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


Supporto per il browser?
Alph.Dev,

1
@ Alph.Dev Conosci caniuse.com?
connexo,

Non riesco a far funzionare la sintassi a 3 punti in node.js. nodo si lamenta per questo.
Klewis,

41

Le soluzioni fornite devono essere modificate per verificare source.hasOwnProperty(property)i for..inloop prima di assegnare - altrimenti, si finisce per copiare le proprietà dell'intera catena di prototipi, che raramente si desidera ...


40

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

Un Object.assignmetodo fa parte dello standard ECMAScript 2015 (ES6) e fa esattamente ciò di cui hai bisogno. ( IEnon supportato)

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

Il metodo Object.assign () viene utilizzato per copiare i valori di tutte le proprietà proprie enumerabili da uno o più oggetti di origine in un oggetto 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;
    }
  });
}

cosa fa "usi rigorosi" nei browser più vecchi o perché esiste? browser che supportano object [defineProperty; chiavi; getOwnPropertyDescriptor; ecc.], non sono esattamente vecchi. Sono solo versioni precedenti di UA 5 gen.5.
Bekim Bacaj,

Grazie per aver chiarito che Objects.assignrestituisce un oggetto unito a cui le altre risposte non lo fanno. Questo è uno stile di programmazione più funzionale.
Sridhar Sarnobat,

36

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

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


4
Si noti che i metodi sopra descritti mutano l'oggetto originale.
Wtower,

Il metodo lodash ha funzionato per me perché volevo unire due oggetti e mantenere le proprietà nidificate più di uno o due livelli in profondità (piuttosto che semplicemente sostituirli / cancellarli).
SamBrick,

true @Wtower. Ho appena avuto un bug per questo. Assicurati che inizi sempre come _.merge ({}, ...
Martin Cremer,

29

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

In questo modo l'area degli oggetti JavaScript si è davvero unita: solo le chiavi todell'oggetto che non sono oggetti stessi verranno sovrascritte from. Tutto il resto sarà davvero unito . Ovviamente puoi cambiare questo comportamento per non sovrascrivere nulla che esiste come solo se 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);

3
Mi ha aiutato molto, il carattere di sottolineatura js davvero ha sovrascritto, invece di unire
David Cumps,

1
Vale la pena notare che typeof array e typeof null restituiranno 'object' e lo romperanno.
Salakar,

@ u01jmg3 vedere la mia risposta qui: stackoverflow.com/questions/27936772/... - la funzione IsObject
Salakar

Questo si interrompe se si utilizza è in modalità rigorosa per progetti
node.js

28

Ecco la mia pugnalata che

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

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

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

Rispetto agli altri che ho testato da questa pagina, questa funzione è veramente ricorsiva (fa una fusione profonda) e imita ciò che jQuery.extend () fa davvero bene. Tuttavia, sarebbe meglio per lui modificare il primo oggetto / argomento in modo che un utente possa decidere se desidera passare un oggetto vuoto {}come primo parametro o se la funzione modifica l'oggetto originale. Pertanto se si cambia dst = {}ad dst = arguments[0]esso cambierà il primo oggetto si passa all'oggetto fusione
Jasdeep Khalsa

3
Ora ha smesso di funzionare in Chrome. Penso che sia una cattiva idea usare tale costruzione toString.call(src) == '[object Object]'per verificare se c'è un oggetto o no. typeof srcè molto meglio. Inoltre trasforma gli array in oggetto (al momento ho un comportamento simile sull'ultimo chrome).
m03geek,

Avevo bisogno di farlo all'interno di un loop avido, ho confrontato questa funzione con quella che stavo usando finora _.merge (oggetto, [fonti]) da lodash (una sorta di sottolineatura lib): la tua funzione è quasi due volte più veloce grazie compagno.
Arnaud Bouchot,

20

Object.assign ()

ECMAScript 2015 (ES6)

Questa è una nuova tecnologia, parte dello standard ECMAScript 2015 (ES6). Le specifiche di questa tecnologia sono state finalizzate, ma controlla la tabella di compatibilità per lo stato di utilizzo e implementazione in vari browser.

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

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

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

Tieni presente che in questo esempio "} {" non deve trovarsi all'interno di una stringa!


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,

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

o come one-liner:objMerge = JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)).replace(/\}\{/, ", "));
Rabbi Shuki Gur,

1
@Charles, scusa ma com'è "questo metodo molto pericoloso" ma anche divertente - questo è il più sicuro possibile 1 ° usa JSON per sollevare pesantemente la lettura del contenuto dell'oggetto, in secondo luogo, usa l'oggetto JSON che è sicuramente il più sicuro macchina ottimizzata per la replica di JSO da una stringa di oggetti compatibile JSON letterale. Ed è anche retrocompatibile con IE8, che è il primo browser ad implementare il suo standard. Mi viene solo da chiedermi "perché oh perché hai dovuto dire una cosa così stupida e cavartela"?
Bekim Bacaj,

Credo che le funzioni nell'oggetto si interrompano qui poiché non è possibile utilizzare stringify, ma possiamo chiarire altri aspetti negativi?
Sì,

18

C'è una libreria chiamata deepmergesu GitHub : sembra che stia ottenendo una certa trazione. È un sistema autonomo, disponibile tramite i gestori dei pacchetti npm e bower.

Sarei propenso a utilizzare o migliorare questo invece di copiare e incollare il codice dalle risposte.


17

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

In questo modo sarai ancora in grado di scorrere le proprietà dei tuoi oggetti senza avere la "estensione" appena creata che otterrai se dovessi creare la proprietà con Object.prototype.extend.

Speriamo che questo aiuti:

Object.defineProperty (Object.prototype, "estende", {
    enumerabile: false,
    valore: funzione (da) {
        var props = Object.getOwnPropertyNames (da);
        var dest = this;
        props.forEach (funzione (nome) {
            if (nome in dest) {
                var destination = Object.getOwnPropertyDescriptor (da, nome);
                Object.defineProperty (dest, nome, destinazione);
            }
        });
        restituire questo;
    }
});

Una volta che hai funzionato, puoi fare:

var obj = {
    nome: 'stack',
    finitura: 'overflow'
}
var sostituzione = {
    nome: 'stock'
};

obj.extend (sostituzione);

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


Aspetta un secondo --- il codice non funziona! :( lo ha inserito in jsperf per confrontarlo con altri algoritmi di fusione, e il codice fallisce - l'estensione della funzione non esiste
Jens Roland il

16

Puoi semplicemente usare jQuery extend

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

jQuery.extend(obj1, obj2);

Ora obj1contiene tutti i valori di obj1eobj2


2
obj1 conterrà tutti i valori, non obj2. Stai estendendo il primo oggetto. jQuery.extend( target [, object1 ] [, objectN ] )
ruuter,

5
Questa domanda non riguarda jQuery. JavaScript! = JQuery
Jorge Riv,

1
Ho cercato su JavaScript, ma questo è un approccio più semplice
Ehsan,

13

Prototype ha questo:

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

obj1.extend(obj2) farà quello che vuoi.


6
Intendevi Object.prototype.extend? In ogni caso, non è una buona idea estendere Object.prototype. -1.
SolutionYogi,

Vieni a pensarci, probabilmente dovrebbe essere Object.prototypee non Object... Buona idea o no, questo è ciò prototype.jsche fa il framework, e quindi se lo stai usando o un altro framework che dipende da esso, Object.prototypesarà già esteso con extend.
effimero

2
Non intendo attaccarti, ma dire che va bene perché prototype.js è un argomento scarso. Prototype.js è popolare ma non significa che siano il modello di ruolo su come eseguire JavaScript. Controlla questa recensione dettagliata della libreria Prototype da parte di un professionista JS. dhtmlkitchen.com/?category=/JavaScript/&date=2008/06/17/…
SolutionYogi

7
A chi importa se è giusto o sbagliato? Se funziona, allora funziona. Aspettare la soluzione perfetta è eccezionale, tranne quando si cerca di mantenere un contratto, acquisire nuovi clienti o rispettare le scadenze. Dai a tutti coloro che sono appassionati di programmare denaro gratis e alla fine faranno tutto nel modo "giusto".
David,

1
Dove avete trovato tutti voi ragazzi Object.prototype? Object.extendnon interferirà con i tuoi oggetti, si limita a collegare una funzione a un Objectoggetto. E obj1.extend(obj2)non è corretto modo di funzione di incendio usare Object.extend(obj1, obj2)insted
artnikpro

13

** L'unione di oggetti è semplice utilizzando Object.assigno l' ...operatore diffusione **

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

Gli oggetti vengono uniti da destra a sinistra, questo significa che gli oggetti che hanno proprietà identiche a quelli alla loro destra verranno sostituiti.

In questo esempio obj2.carsostituisceobj1.car


12

Solo se qualcuno utilizza 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'

10

Ho esteso il metodo di David Coallier:

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

Se l'override è falso, nessuna proprietà viene sovrascritta ma verranno aggiunte nuove proprietà.

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

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 casi di test:

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

Il mio metodo uguale è disponibile qui: Confronto oggetti in JavaScript



9

In Ext JS 4 può essere fatto come segue:

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

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

Vedi unione (oggetto): Oggetto .


1
Attenzione al bug EXTJS-9173 in EXT.Object.merge ancora dopo +2 anni non ancora risolti
Chris

9

Wow .. questo è il primo post StackOverflow che ho visto con più pagine. Scuse per l'aggiunta di un'altra "risposta"

Questo metodo è per ES5 e versioni precedenti : ci sono molte altre risposte rivolte a ES6.

Non ho visto alcun oggetto "profondo" fondersi utilizzando la argumentsproprietà. Ecco la mia risposta: compatta e ricorsiva , che consente di passare argomenti di oggetti illimitati:

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

La parte commentata è facoltativa .. salterà semplicemente gli argomenti passati che non sono oggetti (impedendo errori).

Esempio:

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

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

Questa risposta ora è solo una goccia nell'oceano ...


La risposta più breve che funziona alla grande! Merita più voti!
Jens Törnell,

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

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

Utilizzando jQuery.extend () - Link

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

Usando _.merge () - Link

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

Utilizzando _.extend () - Link

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

Utilizzo di Object.assign () ECMAScript 2015 (ES6) - Link

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

Uscita di tutto

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

Basato sulla risposta di Markus e vsync , questa è una versione estesa. La funzione accetta qualsiasi numero di argomenti. Può essere utilizzato per impostare proprietà sui nodi DOM e creare copie profonde dei valori. Tuttavia, il primo argomento è dato per riferimento.

Per rilevare un nodo DOM, viene utilizzata la funzione isDOMNode () (vedi domanda Stack Overflow JavaScript isDOM - Come si controlla se un oggetto JavaScript è un oggetto DOM? )

È stato testato su Opera 11, Firefox 6, Internet Explorer 8 e 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

Imposta innerHTML e lo stile di un elemento HTML

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

Unisci matrici e oggetti. Si noti che undefined può essere utilizzato per conservare i valori nell'array / oggetto di sinistra.

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

Qualsiasi argomento che non sia un oggetto JavaScript (incluso null) verrà ignorato. Ad eccezione del primo argomento, anche i nodi DOM vengono scartati. Attenzione che le stringhe, create come nuove String () sono in realtà oggetti.

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

Se si desidera unire due oggetti in un nuovo (senza influire su nessuno dei due), fornire {} come primo argomento

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

Modifica (di ReaperSoon):

Per unire anche array

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

Dovresti usare le impostazioni predefinite di lodash Deep

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

5

Con Underscore.js , per unire una matrice di oggetti:

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

Si traduce in:

Object {a: 1, b: 2, c: 3, d: 4}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.