Wie kann ich Eigenschaften von zwei JavaScript-Objekten dynamisch zusammenführen?


2519

Ich muss in der Lage sein, zwei (sehr einfache) JavaScript-Objekte zur Laufzeit zusammenzuführen. Zum Beispiel möchte ich:

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

obj1.merge(obj2);

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

Hat jemand ein Skript dafür oder kennt eine eingebaute Methode, um dies zu tun? Ich brauche keine Rekursion und ich muss keine Funktionen zusammenführen, sondern nur Methoden für flache Objekte.


Es lohnt sich, diese Antwort auf eine ähnliche Frage zu verweisen, die zeigt, wie man "eine Ebene tiefer" zusammenführt. Das heißt, es werden Werte doppelter Schlüssel zusammengeführt (anstatt den ersten Wert mit dem zweiten Wert zu überschreiben), aber es wird nicht weiter darauf zurückgegriffen. IMHO, es ist ein guter sauberer Code für diese Aufgabe.
ToolmakerSteve

Übrigens führen die obersten Antworten eine "flache" Zusammenführung durch: Wenn sowohl in obj1 als auch in obj2 derselbe Schlüssel vorhanden ist, wird der Wert in obj2 beibehalten und der Wert in obj1 wird gelöscht. Wenn beispielsweise das Beispiel der Frage var obj2 = { animal: 'dog', food: 'bone' };wäre, wäre die Zusammenführung { food: 'bone', car: 'ford', animal: 'dog' }. Wenn Sie mit "verschachtelten Daten" arbeiten und eine "Tiefenzusammenführung" wünschen, suchen Sie nach Antworten, die "Tiefenzusammenführung" oder "Rekursion" erwähnen. Wenn Sie Werte haben arrays, verwenden Sie die Option "arrayMerge" von github "TehShrike / deepmerge", wie hier erwähnt .
ToolmakerSteve

Bitte folgen Sie dem folgenden Link stackoverflow.com/questions/171251/… Der Spread-Operator, der die neue Funktion in der ES6-Version ist
Dev216

Antworten:


2907

ECMAScript 2018 Standardmethode

Sie würden Objektverbreitung verwenden :

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

mergedist jetzt die Vereinigung von obj1und obj2. Eigenschaften in obj2überschreiben die 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};

Hier finden Sie auch die MDN-Dokumentation für diese Syntax. Wenn Sie Babel verwenden, benötigen Sie das Babel-Plugin-Transform-Object-Rest-Spread- Plugin, damit es funktioniert.

ECMAScript 2015 (ES6) Standardmethode

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

(siehe MDN JavaScript-Referenz )


Methode für ES5 und früher

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

Beachten Sie, dass dadurch einfach alle Attribute hinzugefügt werden obj2, obj1die möglicherweise nicht Ihren Wünschen entsprechen, wenn Sie die unveränderten weiterhin verwenden möchten obj1.

Wenn Sie ein Framework verwenden, das Ihre Prototypen überfrachtet, müssen Sie mit Überprüfungen wie z. B. besser umgehen hasOwnProperty, aber dieser Code funktioniert in 99% der Fälle.

Beispielfunktion:

/**
 * 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
Dies funktioniert nicht, wenn Objekte dieselben Namensattribute haben und Sie die Attribute auch zusammenführen möchten.
Xiè Jìléi

69
Dies führt nur eine flache Kopie / Zusammenführung durch. Hat das Potenzial, viele Elemente zu zerstören.
Jay Taylor

45
+1 für die Anerkennung, dass einige arme Seelen gezwungen sind, Frameworks zu verwenden, die überall auf ihren Prototypen
scheißen

4
Dies funktionierte bei mir nicht, weil "daher Eigenschaften zugewiesen werden, anstatt nur neue Eigenschaften zu kopieren oder zu definieren. Dies kann dazu führen, dass neue Eigenschaften nicht zu einem Prototyp zusammengeführt werden, wenn die Zusammenführungsquellen Getter enthalten." ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ). Ich musste benutzen var merged = {...obj1, ...obj2}.
Morgler

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

1191

jQuery hat auch ein Dienstprogramm dafür: http://api.jquery.com/jQuery.extend/ .

Entnommen aus der jQuery-Dokumentation:

// 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" }

Der obige Code mutiert das vorhandene Objekt mit dem Namen settings.


Wenn Sie ein neues Objekt erstellen möchten, ohne eines der Argumente zu ändern, verwenden Sie Folgendes:

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
Achtung: Die Variable "Einstellungen" wird jedoch geändert. jQuery gibt keine neue Instanz zurück. Der Grund dafür (und für die Benennung) ist, dass .extend () entwickelt wurde, um Objekte zu erweitern, anstatt Dinge zusammen zu mischen. Wenn Sie ein neues Objekt möchten (z. B. Einstellungen sind Standardeinstellungen, die Sie nicht berühren möchten), können Sie jederzeit jQuery.extend ({}, Einstellungen, Optionen) verwenden.
Webmat

1
Direkt am! Vielen Dank. Wie bereits erwähnt, ist der Name schlecht. Ich habe jQuery-Dokumente zurück und viertens durchsucht und bin nicht darauf gestoßen.
Mike Starov

28
Wohlgemerkt, jQuery.extend hat auch eine tiefe (boolesche) Einstellung. jQuery.extend(true,settings,override)Dies ist wichtig, wenn eine Eigenschaft in Einstellungen ein Objekt enthält und das Überschreiben nur einen Teil dieses Objekts enthält. Anstatt die nicht übereinstimmenden Eigenschaften zu entfernen, wird die Tiefeneinstellung nur dort aktualisiert, wo sie vorhanden ist. Der Standardwert ist false.
Vol7ron

19
Gee Willikers, sie haben wirklich gute Arbeit geleistet, als sie das nannten. Wenn Sie nach einer Möglichkeit suchen, ein Javascript-Objekt zu erweitern, wird es sofort angezeigt! Sie könnten jedoch nicht darauf stoßen, wenn Sie versuchen, Javascript-Objekte zu verschmelzen oder zusammenzufügen.
Derek Greer

2
Sagen Sie den Leuten nicht, dass sie eine Bibliotheksmethode in Betracht ziehen sollen, wenn ein paar Zeilen Vanilla JS das tun, was sie wollen. Es gab eine Zeit vor jQuery!
CrazyMerlin

347

Das Harmony ECMAScript 2015 (ES6) legt fest Object.assign, wer dies tun wird.

Object.assign(obj1, obj2);

Die aktuelle Browserunterstützung wird immer besser . Wenn Sie jedoch für Browser entwickeln, die keine Unterstützung bieten, können Sie eine Polyfüllung verwenden .


35
Beachten Sie, dass dies nur eine flache Zusammenführung ist
Joachim Lous

Ich denke, mittlerweile Object.assignhat ziemlich gute
LazerBass

13
Dies sollte nun die richtige Antwort sein. Heutzutage kompilieren die Leute ihren Code so, dass er mit dem seltenen Browser (IE11) kompatibel ist, der solche Dinge nicht unterstützt. Randnotiz: Wenn Sie obj2 nicht zu obj1 hinzufügen möchten, können Sie ein neues Objekt mitObject.assign({}, obj1, obj2)
Duvrai

6
@Duvrai Berichten zufolge ist IE11 mit rund 18% des Marktanteils ab Juli 2016 definitiv nicht selten .
NanoWizard

3
@Kermani Das OP weist ausdrücklich darauf hin, dass eine Rekursion nicht erforderlich ist. Obwohl es nützlich ist zu wissen, dass diese Lösung eine flache Zusammenführung durchführt, ist diese Antwort so wie sie ist ausreichend. Der Kommentar von JoachimLou hat wohlverdiente positive Stimmen erhalten und liefert diese zusätzlichen Informationen bei Bedarf. Ich denke, der Unterschied zwischen tiefer und flacher Verschmelzung und ähnlichen Themen geht über den Rahmen dieser Diskussion hinaus und eignet sich besser für andere SO-Fragen.
NanoWizard

264

Ich habe nach Code gegoogelt, um Objekteigenschaften zusammenzuführen, und bin hier gelandet. Da es jedoch keinen Code für die rekursive Zusammenführung gab, habe ich ihn selbst geschrieben. (Vielleicht ist jQuery Extend übrigens rekursiv?) Wie auch immer, hoffentlich findet es auch jemand anderes nützlich.

(Jetzt wird der Code nicht verwendet 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;
}

Ein Beispiel

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

Erzeugt Objekt o3 wie

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

11
Schön, aber ich würde zuerst eine Deepcopy der Objekte machen. Auf diese Weise würde auch o1 geändert, da Objekte als Referenz übergeben werden.
Skerit

1
Das war es, wonach ich gesucht habe. Stellen Sie sicher, dass Sie überprüfen, ob obj2.hasOwnProperty (p) ist, bevor Sie zur try catch-Anweisung wechseln. Andernfalls könnte es vorkommen, dass ein anderer Mist von oben in der Prototypenkette zusammengeführt wird.
Adam

3
Danke Markus. Beachten Sie, dass o1 auch von MergeRecursive geändert wird, sodass o1 und o3 am Ende identisch sind. Es ist möglicherweise intuitiver, wenn Sie nichts zurückgeben, sodass der Benutzer weiß, dass das, was er liefert (o1), geändert wird und zum Ergebnis wird. In Ihrem Beispiel kann es auch sinnvoll sein, etwas zu o2 hinzuzufügen, das ursprünglich nicht in o1 enthalten war, um zu zeigen, dass o2-Eigenschaften in o1 eingefügt werden (eine andere Person hat unten einen alternativen Code eingereicht, der Ihr Beispiel kopiert, aber ihre bricht ab, wenn o2 keine Eigenschaften enthält in o1 - aber deins funktioniert in dieser Hinsicht).
Pfannkuchen

7
Dies ist eine gute Antwort, aber ein paar Streitfragen: 1) Keine Logik sollte auf Ausnahmen basieren; In diesem Fall wird jede ausgelöste Ausnahme mit unvorhersehbaren Ergebnissen abgefangen. 2) Ich stimme dem Kommentar von @ pancake zu, dass nichts zurückgegeben wird, da das ursprüngliche Objekt geändert wurde. Hier ist ein jsFiddle-Beispiel ohne die Ausnahmelogik: jsfiddle.net/jlowery2663/z8at6knn/4 - Jeff Lowery
Jeff Lowery

3
Außerdem wird obj2 [p] .constructor == Array in jedem Fall benötigt, in dem es ein Array von Objekten gibt.
Der Komponist

175

Beachten Sie, dass underscore.jsdie extendMethode dies in einem Einzeiler tut:

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

36
Dieses Beispiel ist in Ordnung, da es sich um anonyme Objekte handelt. Ist dies jedoch nicht der Fall, gilt hier der Kommentar von webmat in der jQuery- Antwortwarnung zu Mutationen, da der Unterstrich auch das Zielobjekt mutiert . Wie die jQuery-Antwort, um dies ohne Mutation zu tun, verschmelzen Sie einfach zu einem leeren Objekt:_({}).extend(obj1, obj2);
Abe Voelker

8
Es gibt noch eine andere, die relevant sein kann, je nachdem, was Sie erreichen _.defaults(object, *defaults)möchten : "Füllen Sie null und undefinierte Eigenschaften in ein Objekt mit Werten aus den Standardobjekten aus und geben Sie das Objekt zurück."
Conny

Viele Leute haben sich zum Mutieren des Zielobjekts geäußert, aber anstatt eine Methode ein neues erstellen zu lassen, lautet ein allgemeines Muster (z. B. im Dojo) dojo.mixin (dojo.clone (myobj), {newpropertys: ", um hinzuzufügen zu myobj "})
Colin D

7
Der Unterstrich kann eine beliebige Anzahl von Objekten annehmen, sodass Sie eine Mutation des ursprünglichen Objekts vermeiden können, indem Sie dies tun var mergedObj = _.extend({}, obj1, obj2).
Lobati

84

Ähnlich wie bei jQuery extens () haben Sie in AngularJS dieselbe Funktion :

// 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 Ich denke, es wäre eine Option, wenn Sie keine Bibliothek nur für diese fn hinzufügen möchten.
Juanmf

67

Ich muss heute Objekte zusammenführen, und diese Frage (und Antworten) hat mir sehr geholfen. Ich habe einige der Antworten ausprobiert, aber keine davon entsprach meinen Anforderungen. Deshalb habe ich einige der Antworten kombiniert, selbst etwas hinzugefügt und eine neue Zusammenführungsfunktion entwickelt. Hier ist es:

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

Einige Anwendungsbeispiele:

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
Gute Antwort, aber ich denke, wenn eine Eigenschaft in der ersten und in der zweiten vorhanden ist und Sie versuchen, ein neues Objekt zu erstellen, indem Sie zuerst und zweitens zusammenführen, gehen die im ersten Objekt vorhandenen eindeutigen Objekte verloren
Strikers

2
Aber ist es nicht erwartetes Verhalten? Der Zweck dieser Funktion besteht darin, Werte in der Argumentreihenfolge zu überschreiben. Als nächstes wird der Strom überschrieben. Wie können Sie einzigartige Werte bewahren? Was erwarten Sie von meinem Beispiel merge({}, t1, { key1: 1 })?
Emre Erkan

Ich erwarte, nur nicht objekttypisierte Werte zusammenzuführen.
AGamePlayer

schlägt fehl, wenn das erwartete Ergebnis wie folgt lautet: gist.github.com/ceremcem/a54f90923c6e6ec42c7daf24cf2768bd
Zeremonie

Dies funktionierte für mich im strengen Modus für mein node.js-Projekt. Bisher ist dies die beste Antwort auf diesen Beitrag.
Klewis

48

Sie können Objektverbreitungseigenschaften verwenden - derzeit ein ECMAScript-Vorschlag der Stufe 3.

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

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


Browser-Unterstützung?
Alph.Dev

1
@ Alph.Dev Sie kennen caniuse.com?
Connexo

Ich kann die 3-Punkt-Syntax in node.js nicht zum Laufen bringen. Knoten beschwert sich darüber.
Klewis

41

Die angegebenen Lösungen sollten geändert werden, um source.hasOwnProperty(property)die for..inSchleifen vor dem Zuweisen einzuchecken. Andernfalls kopieren Sie am Ende die Eigenschaften der gesamten Prototypenkette, was selten erwünscht ist ...


40

Führen Sie die Eigenschaften von N Objekten in einer Codezeile zusammen

Eine Object.assignMethode ist Teil des ECMAScript 2015 (ES6) -Standards und macht genau das, was Sie brauchen. ( IEnicht unterstützt)

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

Die Object.assign () -Methode wird verwendet, um die Werte aller aufzählbaren eigenen Eigenschaften von einem oder mehreren Quellobjekten in ein Zielobjekt zu kopieren.

Weiterlesen...

Die Polyfüllung zur Unterstützung älterer Browser:

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

Was macht 'use strict' in älteren Browsern oder warum ist es überhaupt da? Browser, die object [defineProperty; Schlüssel; getOwnPropertyDescriptor; etc] sind nicht gerade alt. Sie sind nur frühere Versionen von Gen.5 UAs.
Bekim Bacaj

Vielen Dank für die Klarstellung, dass Objects.assignein zusammengeführtes Objekt zurückgegeben wird, was die anderen Antworten nicht tun. Das ist mehr funktionaler Programmierstil.
Sridhar Sarnobat

36

Die folgenden beiden sind wahrscheinlich ein guter Ausgangspunkt. lodash hat auch eine Anpassungsfunktion für diese besonderen Bedürfnisse!

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


4
Bitte beachten Sie, dass die oben genannten Methoden das ursprüngliche Objekt mutieren.
Wtower

Die lodash-Methode funktionierte für mich, da ich zwei Objekte zusammenführen und Eigenschaften beibehalten wollte, die mehr als eine oder zwei Ebenen tief verschachtelt waren (anstatt sie nur zu ersetzen / zu löschen).
SamBrick

true @Wtower. Hatte gerade einen Fehler deswegen. Am besten stellen Sie sicher, dass es immer wie _.merge beginnt ({}, ...
Martin Cremer

29

Übrigens, was Sie alle tun, ist, Eigenschaften zu überschreiben, nicht zu verschmelzen ...

So wird der JavaScript-Objektbereich wirklich zusammengeführt: Nur Schlüssel im toObjekt, die selbst keine Objekte sind, werden von überschrieben from. Alles andere wird wirklich zusammengeführt . Natürlich können Sie dieses Verhalten ändern, um nichts zu überschreiben, was nur existiert, wenn to[n] is undefined, etc ...:

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

Verwendungszweck:

var merged = realMerge(obj1, obj2);

3
Hat mir sehr geholfen, der Unterstrich js verlängern hat tatsächlich überschrieben, anstatt zu verschmelzen
David Cumps

1
Es ist erwähnenswert, dass typeof array und typeof null 'object' zurückgeben und dies unterbrechen.
Salakar

@ u01jmg3 siehe meine Antwort hier: stackoverflow.com/questions/27936772/… - die isObject-Funktion
Salakar

Dies bricht ab, wenn Sie im strengen Modus für node.js Projekte verwenden
klewis

28

Hier ist mein Stich was

  1. Unterstützt Deep Merge
  2. Mutiert keine Argumente
  3. Nimmt eine beliebige Anzahl von Argumenten an
  4. Erweitert den Objektprototyp nicht
  5. Hängt nicht von einer anderen Bibliothek ab ( jQuery , MooTools , Underscore.js usw.)
  6. Beinhaltet die Überprüfung auf hasOwnProperty
  7. Ist kurz :)

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

Beispiel:

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

Im Vergleich zu den anderen, die ich auf dieser Seite getestet habe, ist diese Funktion wirklich rekursiv (führt eine tiefe Zusammenführung durch) und ahmt nach, was jQuery.extend () wirklich gut macht. Es ist jedoch besser, das erste Objekt / Argument zu ändern, damit ein Benutzer entscheiden kann, ob er ein leeres Objekt {}als ersten Parameter übergeben oder die Funktion das ursprüngliche Objekt ändern lassen möchte . Wenn Sie also dst = {}zu dst = arguments[0]ihm wechseln , ändert sich das erste Objekt, das Sie an das zusammengeführte Objekt übergeben
Jasdeep Khalsa

3
Jetzt funktioniert es nicht mehr in Chrom. Ich denke, es ist eine schlechte Idee, eine solche Konstruktion zu verwenden, toString.call(src) == '[object Object]'um zu überprüfen, ob es ein Objekt gibt oder nicht. typeof srcist viel besser. Außerdem verwandelt es Arrays in Objekte (zumindest habe ich ein solches Verhalten bei neuestem Chrom).
m03geek

Ich musste dies in einer gierigen Schleife tun. Ich habe diese Funktion mit der verglichen, die ich bisher verwendet habe. _.Merge (Objekt, [Quellen]) von lodash (eine Art Unterstrich lib): Ihre Funktion ist fast doppelt so schnell danke Kumpel.
Arnaud Bouchot

20

Object.assign ()

ECMAScript 2015 (ES6)

Dies ist eine neue Technologie, die Teil des ECMAScript 2015 (ES6) -Standards ist. Die Spezifikation dieser Technologie wurde fertiggestellt. Überprüfen Sie jedoch die Kompatibilitätstabelle auf Verwendung und Implementierungsstatus in verschiedenen Browsern.

Die Object.assign () -Methode wird verwendet, um die Werte aller aufzählbaren eigenen Eigenschaften von einem oder mehreren Quellobjekten in ein Zielobjekt zu kopieren. Das Zielobjekt wird zurückgegeben.

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

Für nicht zu komplizierte Objekte können Sie JSON verwenden :

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_/

Beachten Sie jedoch, dass in diesem Beispiel "} {" nicht innerhalb einer Zeichenfolge vorkommen darf !


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

oder als Einzeiler:objMerge = JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)).replace(/\}\{/, ", "));
Rabbi Shuki Gur

1
@ Charles, sorry, aber wie ist "diese Methode sehr gefährlich", macht aber auch Spaß - dies ist die sicherste Möglichkeit. Erstens wird JSON verwendet, um das Lesen des Objektinhalts zu erleichtern. Zweitens wird JSON-Objekt verwendet, das auf jeden Fall die sicherste ist Maschine optimiert für die Reparatur von JSO aus einem JSON-kompatiblen Objektzeichenfolgenliteral. Und ist auch abwärtskompatibel mit IE8, dem ersten Browser, der seinen Standard implementiert. Ich frage mich nur: "Warum, oh, warum musstest du so etwas Dummes sagen und damit davonkommen?"
Bekim Bacaj

Ich glaube, dass Funktionen im Objekt hier zusammenbrechen werden, da stringify nicht verwendet werden kann, aber können wir andere Nachteile klären?
yeahdixon

18

Es gibt eine Bibliothek mit dem Namen deepmergeauf GitHub : Das scheint einige Traktion zu bekommen. Es ist ein eigenständiges Programm, das sowohl über den npm- als auch den bower-Paketmanager erhältlich ist.

Ich wäre geneigt, dies zu verwenden oder zu verbessern, anstatt Code aus Antworten einzufügen.


17

Der beste Weg, dies zu tun, besteht darin, eine geeignete Eigenschaft hinzuzufügen, die mit Object.defineProperty nicht aufzählbar ist.

Auf diese Weise können Sie weiterhin über Ihre Objekteigenschaften iterieren, ohne die neu erstellte "Erweiterung" zu haben, die Sie erhalten würden, wenn Sie die Eigenschaft mit Object.prototype.extend erstellen würden.

Hoffentlich hilft das:

Object.defineProperty (Object.prototype, "verlängern", {
    aufzählbar: falsch,
    Wert: Funktion (von) {
        var props = Object.getOwnPropertyNames (from);
        var dest = this;
        props.forEach (Funktion (Name) {
            if (name in dest) {
                var destination = Object.getOwnPropertyDescriptor (from, name);
                Object.defineProperty (Ziel, Name, Ziel);
            }}
        });
        gib das zurück;
    }}
});

Sobald Sie das zum Laufen gebracht haben, können Sie Folgendes tun:

var obj = {
    Name: 'Stapel',
    Ende: 'Überlauf'
}}
var Ersatz = {
    Name: 'Lager'
};

obj.extend (Ersatz);

Ich habe gerade hier einen Blog-Beitrag darüber geschrieben: http://onemoredigit.com/post/1527191998/extending-objects-in-node-js


Warten Sie eine Sekunde --- der Code funktioniert nicht! :( fügte es in jsperf ein, um es mit anderen Zusammenführungsalgorithmen zu vergleichen, und der Code schlägt fehl - Funktionserweiterung existiert nicht
Jens Roland

16

Sie können einfach jQuery verwenden extend

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

jQuery.extend(obj1, obj2);

obj1Enthält jetzt alle Werte von obj1undobj2


2
obj1 enthält alle Werte, nicht obj2. Sie erweitern das erste Objekt. jQuery.extend( target [, object1 ] [, objectN ] )
Ruuter

5
Bei dieser Frage geht es nicht um jQuery. JavaScript! = JQuery
Jorge Riv

1
Ich habe nach JavaScript gegoogelt, aber dies ist ein einfacher Ansatz
Ehsan

13

Prototyp hat dies:

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

obj1.extend(obj2) wird tun was du willst.


6
Sie meinten Object.prototype.extend? In jedem Fall ist es keine gute Idee, Object.prototype zu erweitern. -1.
SolutionYogi

Wenn Sie darüber nachdenken, sollte es wahrscheinlich sein Object.prototypeund nicht Object... Gute Idee oder nicht, dies ist, was das prototype.jsFramework tut, und wenn Sie es verwenden oder ein anderes Framework, das davon abhängt, Object.prototypewird es bereits erweitert extend.
Ephemient

2
Ich will dich nicht harfen, aber zu sagen, dass es in Ordnung ist, weil prototype.js es tut, ist ein schlechtes Argument. Prototype.js ist beliebt, bedeutet aber nicht, dass sie das Vorbild für JavaScript sind. Überprüfen Sie diese detaillierte Überprüfung der Prototype-Bibliothek durch einen JS-Profi. dhtmlkitchen.com/?category=/JavaScript/&date=2008/06/17/…
SolutionYogi

7
Wen interessiert es, ob es richtig oder falsch ist? Wenn es funktioniert, funktioniert es. Das Warten auf die perfekte Lösung ist großartig, außer wenn Sie versuchen, einen Vertrag zu halten, neue Kunden zu gewinnen oder Termine einzuhalten. Geben Sie jedem hier, der leidenschaftlich gerne kostenlos Geld programmiert, und er wird schließlich alles "richtig" machen.
David

1
Wo habt ihr alle gefunden Object.prototype? Object.extendwird Ihre Objekte nicht stören, sondern nur eine Funktion an ein ObjectObjekt anhängen. Und obj1.extend(obj2)ist nicht der richtige Weg, um Funktion zu feuern, verwenden Sie Object.extend(obj1, obj2)insted
artnikpro

13

** Das Zusammenführen von Objekten ist einfach mit Object.assigndem Spread- ...Operator **

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

Die Objekte werden von rechts nach links zusammengeführt. Dies bedeutet, dass Objekte mit identischen Eigenschaften wie die Objekte auf der rechten Seite überschrieben werden.

In diesem Beispiel obj2.carüberschreibtobj1.car



10

Ich habe die Methode von David Coallier erweitert:

  • Es wurde die Möglichkeit hinzugefügt, mehrere Objekte zusammenzuführen
  • Unterstützt tiefe Objekte
  • Override-Parameter (wird erkannt, wenn der letzte Parameter ein Boolescher Wert ist)

Wenn override false ist, wird keine Eigenschaft überschrieben, es werden jedoch neue Eigenschaften hinzugefügt.

Verwendung: obj.merge (führt zusammen ... [, überschreiben]);

Hier ist mein Code:

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

Beispiele und Testfälle:

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

Meine Methode equals finden Sie hier: Objektvergleich in JavaScript



9

In Ext JS 4 kann dies wie folgt durchgeführt werden:

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

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

Siehe Zusammenführen (Objekt): Objekt .


1
Vorsicht vor dem Fehler EXTJS-9173 in EXT.Object.merge, der nach +2 Jahren noch nicht behoben wurde
Chris

9

Wow .. dies ist der erste StackOverflow-Beitrag, den ich mit mehreren Seiten gesehen habe. Entschuldigung für das Hinzufügen einer weiteren "Antwort"

Diese Methode ist für ES5 und früher - es gibt viele andere Antworten für ES6.

Ich habe kein "tiefes" Objekt gesehen, das unter Verwendung der argumentsEigenschaft zusammengeführt wurde. Hier ist meine Antwort - kompakt und rekursiv , sodass unbegrenzte Objektargumente übergeben werden können:

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

Der auskommentierte Teil ist optional. Es werden einfach übergebene Argumente übersprungen, die keine Objekte sind (um Fehler zu vermeiden).

Beispiel:

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

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

Diese Antwort ist jetzt nur noch ein Tropfen auf den heißen Stein ...


Die kürzeste Antwort, die großartig funktioniert! Es verdient mehr Stimmen!
Jens Törnell

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

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

Verwenden von jQuery.extend () - Link

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

Verwenden von _.merge () - Link

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

Verwenden von _.extend () - Link

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

Verwenden von Object.assign () ECMAScript 2015 (ES6) - Link

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

Ausgabe von allen

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

Basierend auf der Antwort von Markus und vsync ist dies eine erweiterte Version. Die Funktion akzeptiert eine beliebige Anzahl von Argumenten. Es kann verwendet werden, um Eigenschaften für DOM festzulegen Knoten und tiefe Kopien von Werten zu . Das erste Argument wird jedoch durch Bezugnahme angegeben.

Um einen DOM-Knoten zu erkennen, wird die Funktion isDOMNode () verwendet (siehe Frage zum Stapelüberlauf JavaScript isDOM - Wie können Sie überprüfen, ob ein JavaScript-Objekt ein DOM-Objekt ist? )

Es wurde in Opera 11, Firefox 6, Internet Explorer 8 und Google Chrome 16 getestet .

Code

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

Einige Beispiele

Legen Sie innerHTML und den Stil eines HTML-Elements fest

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

Arrays und Objekte zusammenführen. Beachten Sie, dass undefined verwendet werden kann, um Werte im linken Array / Objekt beizubehalten.

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

Alle Argumente, die kein JavaScript-Objekt sind (einschließlich null), werden ignoriert. Mit Ausnahme des ersten Arguments werden auch DOM-Knoten verworfen. Beachten Sie, dass Zeichenfolgen, die wie neue Zeichenfolgen () erstellt wurden, tatsächlich Objekte sind.

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

Wenn Sie zwei Objekte zu einem neuen zusammenführen möchten (ohne eines der beiden zu beeinflussen), geben Sie {} als erstes Argument an

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

Bearbeiten (von ReaperSoon):

Arrays auch zusammenführen

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

Sie sollten lodash die Verwendung defaultsDeep

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

5

Führen Sie mit Underscore.js Folgendes aus , um ein Array von Objekten zusammenzuführen:

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

Es fuehrt zu:

Object {a: 1, b: 2, c: 3, d: 4}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.