Comment fusionner dynamiquement les propriétés de deux objets JavaScript?

2522

J'ai besoin de pouvoir fusionner deux (très simples) objets JavaScript au moment de l'exécution. Par exemple, je voudrais:

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

obj1.merge(obj2);

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

Quelqu'un at-il un script pour cela ou connaît-il une manière intégrée de le faire? Je n'ai pas besoin de récursivité, et je n'ai pas besoin de fusionner de fonctions, juste des méthodes sur des objets plats.

JC Grubbs
la source
Il vaut la peine de noter cette réponse sur une question similaire , qui montre comment fusionner "un niveau plus bas". C'est-à-dire qu'il fusionne les valeurs des clés en double (au lieu de remplacer la première valeur par la deuxième valeur), mais ne récapitule pas plus loin que cela. À mon humble avis, son bon code propre pour cette tâche.
ToolmakerSteve
BTW, les quelques premières réponses font une fusion "superficielle": si la même clé existe à la fois dans obj1 et obj2, la valeur dans obj2 est conservée, la valeur dans obj1 est supprimée. Par exemple, si l'exemple de la question avait var obj2 = { animal: 'dog', food: 'bone' };, la fusion serait { food: 'bone', car: 'ford', animal: 'dog' }. Si vous travaillez avec des "données imbriquées" et souhaitez une "fusion profonde", recherchez les réponses qui mentionnent "fusion profonde" ou "récursivité". Si vous avez des valeurs qui sont arrays, alors utilisez l'option "arrayMerge" du github "TehShrike / deepmerge", comme mentionné ici .
ToolmakerSteve
Veuillez suivre le lien ci-dessous stackoverflow.com/questions/171251/… L'opérateur de propagation qui est la nouvelle fonctionnalité de la version
ES6

Réponses:

2910

Méthode standard ECMAScript 2018

Vous utiliseriez la propagation d'objets :

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

mergedest maintenant l'union de obj1et obj2. Les propriétés de obj2remplaceront celles de 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};

Voici également la documentation MDN de cette syntaxe. Si vous utilisez babel, vous aurez besoin du plugin babel-plugin-transform-object-rest-spread pour que cela fonctionne.

ECMAScript 2015 (ES6) Méthode standard

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

(voir référence JavaScript MDN )


Méthode pour ES5 et versions antérieures

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

Notez que cela simplement ajouter tous les attributs de obj2à obj1qui pourrait ne pas être ce que vous voulez si vous voulez continuer à utiliser la non modifiée obj1.

Si vous utilisez un cadre qui craque partout dans vos prototypes, vous devez devenir plus amateur avec des vérifications comme hasOwnProperty, mais ce code fonctionnera dans 99% des cas.

Exemple de fonction:

/**
 * 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;
}
Derek Ziemba
la source
90
Cela ne fonctionne pas si les objets ont des attributs de même nom et que vous souhaitez également fusionner les attributs.
Xiè Jìléi
69
Cela ne fait qu'une copie / fusion superficielle. A le potentiel de s'encombrer de nombreux éléments.
Jay Taylor
45
+1 pour avoir reconnu que certaines pauvres âmes sont obligées d'utiliser des frameworks qui merdent partout dans leurs prototypes ...
thejonwithnoh
4
Cela n'a pas fonctionné pour moi car "Par conséquent, il attribue des propriétés par rapport à la simple copie ou définition de nouvelles propriétés. Cela peut le rendre inapproprié pour la fusion de nouvelles propriétés dans un prototype si les sources de fusion contiennent des getters." ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ). Je devais utiliser var merged = {...obj1, ...obj2}.
morgler
2
@ Ze'ev{...obj1, ...{animal: 'dog'}}
backslash112
1191

jQuery a également un utilitaire pour cela: http://api.jquery.com/jQuery.extend/ .

Tiré de la documentation 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" }

Le code ci-dessus mute l' objet existant nommé settings.


Si vous souhaitez créer un nouvel objet sans modifier aucun des arguments, utilisez ceci:

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.
Avdi
la source
166
Attention: la variable "settings" sera cependant modifiée. jQuery ne renvoie pas de nouvelle instance. La raison de cela (et de la dénomination) est que .extend () a été développé pour étendre des objets, plutôt que pour fusionner des objets. Si vous voulez un nouvel objet (par exemple, les paramètres sont des valeurs par défaut que vous ne voulez pas toucher), vous pouvez toujours jQuery.extend ({}, paramètres, options);
webmat
1
Tout de suite! Merci beaucoup. Comme indiqué précédemment, il est mal nommé. J'ai recherché des documents jQuery en arrière et quatrième et ne l'ai pas rencontré.
Mike Starov
28
Attention, jQuery.extend a également un paramètre profond (booléen). jQuery.extend(true,settings,override), ce qui est important si une propriété des paramètres contient un objet et que la substitution n'a qu'une partie de cet objet. Au lieu de supprimer les propriétés inégalées, le paramètre profond ne se mettra à jour que là où il existe. Le défaut est faux.
vol7ron
19
Gee willikers, ils ont vraiment fait un bon travail en nommant cela. Si vous recherchez comment étendre un objet Javascript, cela arrive tout de suite! Vous pourriez ne pas le toucher si vous essayez de fusionner ou d'assembler des objets Javascript.
Derek Greer
2
Ne dites pas aux gens d'envisager d'utiliser une méthode de bibliothèque quand quelques lignes de vanilla JS feront ce qu'elles veulent. Il fut un temps avant jQuery!
CrazyMerlin
348

Le Harmony ECMAScript 2015 (ES6) spécifie Object.assignqui le fera.

Object.assign(obj1, obj2);

La prise en charge actuelle des navigateurs s'améliore , mais si vous développez pour des navigateurs non pris en charge, vous pouvez utiliser un polyfill .

NanoWizard
la source
35
Notez que ce n'est qu'une fusion superficielle
Joachim Lous
Je pense que pendant ce temps Object.assigna une couverture assez décente: kangax.github.io/compat-table/es6/…
LazerBass
13
Cela devrait maintenant être la bonne réponse. De nos jours, les gens compilent leur code pour être compatible avec le navigateur rare (IE11) qui ne prend pas en charge ce genre de chose. Note latérale: si vous ne voulez pas ajouter obj2 à obj1, vous pouvez renvoyer un nouvel objet en utilisantObject.assign({}, obj1, obj2)
Duvrai
6
@Duvrai Selon les rapports que j'ai vus, IE11 n'est certainement pas rare à environ 18% de la part de marché en juillet 2016.
NanoWizard
3
@Kermani L'OP souligne spécifiquement que la récursivité n'est pas nécessaire. Par conséquent, bien qu'il soit utile de savoir que cette solution effectue une fusion superficielle, cette réponse est suffisante telle quelle. Le commentaire de JoachimLou a reçu des votes positifs bien mérités et fournit ces informations supplémentaires en cas de besoin. Je pense que la différence entre une fusion profonde et superficielle et des sujets similaires dépasse la portée de cette discussion et est mieux adaptée à d'autres questions SO.
NanoWizard
264

J'ai recherché du code pour fusionner les propriétés des objets sur Google et je me suis retrouvé ici. Cependant, comme il n'y avait pas de code pour la fusion récursive, je l'ai écrit moi-même. (Peut-être que jQuery extend est récursif BTW?) Quoi qu'il en soit, j'espère que quelqu'un d'autre le trouvera également utile.

(Maintenant, le code n'utilise pas 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;
}

Un exemple

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

Produit un objet o3 comme

o3 = {  a : 10,
        b : 2,
        c : {
          ca : 10,
          cb : 20,
          cc : { 
            cca : 101,
            ccb : 202 } } };
Markus
la source
11
Bien, mais je ferais d'abord une copie profonde des objets. De cette façon, o1 serait également modifié, car les objets sont passés par référence.
skerit du
1
C'était ce que je cherchais. Assurez-vous de vérifier si obj2.hasOwnProperty (p) avant de passer à l'instruction try catch. Sinon, vous pourriez vous retrouver avec d'autres conneries fusionnées de plus haut dans la chaîne de prototypes.
Adam
3
Merci Markus. Notez que o1 est également modifié par MergeRecursive, donc à la fin, o1 et o3 sont identiques. Cela peut être plus intuitif si vous ne retournez rien, afin que l'utilisateur sache que ce qu'il fournit (o1) est modifié et devient le résultat. De plus, dans votre exemple, il pourrait être utile d'ajouter quelque chose dans o2 qui n'est pas à l'origine dans o1, pour montrer que les propriétés o2 sont insérées dans o1 (quelqu'un d'autre ci-dessous a soumis un code alternatif copiant votre exemple, mais le leur se rompt lorsque o2 contient des propriétés non en o1 - mais le vôtre fonctionne à cet égard).
crêpe
7
C'est une bonne réponse, mais quelques problèmes: 1) Aucune logique ne devrait être basée sur des exceptions; dans ce cas, toute exception levée sera prise avec des résultats imprévisibles. 2) Je suis d'accord avec le commentaire de @ pancake sur le fait de ne rien retourner, car l'objet d'origine est modifié. Voici un exemple jsFiddle sans la logique d'exception: jsfiddle.net/jlowery2663/z8at6knn/4 - Jeff Lowery
Jeff Lowery
3
De plus, obj2 [p] .constructor == Un tableau est nécessaire dans tous les cas où il existe un tableau d'objets.
The Composer
175

Notez que underscore.jsla extendméthode 's fait cela dans une seule ligne:

_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}
Industriel
la source
36
Cet exemple est parfait car vous avez affaire à des objets anonymes, mais si ce n'est pas le cas, le commentaire du webmat dans l' avertissement de réponse jQuery sur les mutations s'applique ici, car le soulignement mute également l'objet de destination . Comme la réponse jQuery, pour faire cela sans mutation, il suffit de fusionner en un objet vide:_({}).extend(obj1, obj2);
Abe Voelker
8
Il y en a un autre qui pourrait être pertinent en fonction de ce que vous essayez de réaliser: _.defaults(object, *defaults)"Remplissez les propriétés nulles et non définies dans l'objet avec les valeurs des objets par défaut et renvoyez l'objet."
conny
Beaucoup de gens ont commenté la mutation de l'objet de destination, mais plutôt que d'avoir une méthode pour en créer un nouveau, un modèle courant (dans dojo par exemple) est de dire dojo.mixin (dojo.clone (myobj), {newpropertys: "to add à myobj "})
Colin D
7
Le soulignement peut prendre un nombre arbitraire d'objets, vous pouvez donc éviter la mutation de l'objet d'origine en faisant var mergedObj = _.extend({}, obj1, obj2).
lobati
84

Semblable à jQuery extend (), vous avez la même fonction dans 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);
AndreasE
la source
1
@naXa Je pense que ce serait une option si vous ne voulez pas ajouter une bibliothèque juste pour ce fn.
juanmf
67

J'ai besoin de fusionner des objets aujourd'hui, et cette question (et ses réponses) m'a beaucoup aidé. J'ai essayé certaines des réponses, mais aucune d'entre elles ne correspondait à mes besoins, j'ai donc combiné certaines des réponses, ajouté moi-même quelque chose et proposé une nouvelle fonction de fusion. C'est ici:

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

Quelques exemples d'utilisations:

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 }));
Emre Erkan
la source
3
bonne réponse mais je pense que si une propriété est présente dans le premier et dans le second et que vous essayez de créer un nouvel objet en fusionnant le premier et le second, alors les uniques présents dans le premier objet seront perdus
Strikers
2
Mais n'est-ce pas un comportement attendu? Le but de cette fonction est de remplacer les valeurs dans l'ordre des arguments. Le suivant remplacera le courant. Comment pouvez-vous préserver des valeurs uniques? D'après mon exemple, qu'attendez-vous de merge({}, t1, { key1: 1 })?
Emre Erkan
Mon attente est de fusionner uniquement les valeurs non typées objet.
AGamePlayer
échoue si le résultat attendu est le suivant: gist.github.com/ceremcem/a54f90923c6e6ec42c7daf24cf2768bd
ceremcem
Cela a fonctionné pour moi en mode strict pour mon projet node.js. Jusqu'à présent, c'est la meilleure réponse sur ce post.
klewis
48

Vous pouvez utiliser les propriétés de répartition des objets - actuellement une proposition ECMAScript d'étape 3.

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

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

Jaime Asm
la source
Prise en charge du navigateur?
Alph.Dev
1
@ Alph.Dev Vous connaissez caniuse.com?
connexo
Je n'arrive pas à faire fonctionner la syntaxe à 3 points dans node.js. le nœud s'en plaint.
klewis
41

Les solutions données doivent être modifiées pour archiver source.hasOwnProperty(property)les for..inboucles avant d'affecter - sinon, vous finissez par copier les propriétés de l'ensemble de la chaîne de prototypes, ce qui est rarement souhaité ...

Christoph
la source
40

Fusionner les propriétés de N objets dans une seule ligne de code

Une Object.assignméthode fait partie de la norme ECMAScript 2015 (ES6) et fait exactement ce dont vous avez besoin. ( IEnon pris en charge)

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

La méthode Object.assign () est utilisée pour copier les valeurs de toutes les propriétés propres énumérables d'un ou plusieurs objets source vers un objet cible.

Lire la suite...

Le polyfill pour supporter les anciens navigateurs:

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;
    }
  });
}
Eugene Tiurin
la source
qu'est-ce que 'use strict' fait dans les anciens navigateursm ou pourquoi est-il là? navigateurs prenant en charge object [defineProperty; clés; getOwnPropertyDescriptor; etc], ne sont pas exactement vieux. Ce ne sont que des versions antérieures des UA de génération 5.
Bekim Bacaj
Merci d'avoir clarifié ce qui Objects.assignrenvoie un objet fusionné auquel les autres réponses ne répondent pas. C'est un style de programmation plus fonctionnel.
Sridhar Sarnobat
36

Les deux suivants sont probablement un bon point de départ. lodash a également une fonction de personnalisation pour ces besoins spéciaux!

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

applications
la source
4
Veuillez noter que les méthodes ci-dessus mutent l'objet d'origine.
Wtower
La méthode lodash a fonctionné pour moi car je voulais fusionner deux objets et conserver les propriétés imbriquées à plus d'un ou deux niveaux de profondeur (plutôt que de simplement les remplacer / les essuyer).
SamBrick
vrai @Wtower. J'ai juste eu un bug à cause de ça. Assurez-vous qu'il commence toujours comme _.merge ({}, ...
Martin Cremer
29

Soit dit en passant, ce que vous faites, c'est écraser les propriétés, pas fusionner ...

C'est ainsi que la zone des objets JavaScript a vraiment fusionné: seules les clés de l' toobjet qui ne sont pas des objets eux-mêmes seront écrasées par from. Tout le reste sera vraiment fusionné . Bien sûr, vous pouvez modifier ce comportement pour ne pas écraser tout ce qui existe comme si 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;
};

Usage:

var merged = realMerge(obj1, obj2);
Andreas Linden
la source
3
M'a beaucoup aidé, le prolongement de js de soulignement a effectivement écrasé, au lieu de fusionner
David Cumps
1
Il convient de noter que typeof array et typeof null renverront «objet» et rompront cela.
Salakar
@ u01jmg3 voir ma réponse ici: stackoverflow.com/questions/27936772/… - la fonction
isObject
Cela casse si vous utilisez est en mode strict pour les projets node.js
klewis
28

Voici mon coup de couteau qui

  1. Prend en charge la fusion profonde
  2. Ne mute pas les arguments
  3. Prend n'importe quel nombre d'arguments
  4. N'étend pas le prototype d'objet
  5. Ne dépend pas d'une autre bibliothèque ( jQuery , MooTools , Underscore.js , etc.)
  6. Comprend la vérification de hasOwnProperty
  7. C'est court :)

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

Exemple:

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
    };
*/
Paul Spaulding
la source
Comparée aux autres que j'ai testées sur cette page, cette fonction est vraiment récursive (fait une fusion profonde) et imite ce que jQuery.extend () fait vraiment bien. Cependant, il serait préférable qu'il modifie le premier objet / argument afin qu'un utilisateur puisse décider s'il souhaite passer un objet vide {}comme premier paramètre ou que la fonction modifie l'objet d'origine. Par conséquent , si vous changez dst = {}de dst = arguments[0]cela va changer le premier objet que vous passez à l'objet fusionné
Jasdeep Khalsa
3
Maintenant, il ne fonctionne plus en chrome. Je pense que c'est une mauvaise idée d'utiliser une telle construction toString.call(src) == '[object Object]'pour vérifier s'il y a un objet ou non. typeof srcc'est beaucoup mieux. De plus, il transforme les tableaux en objets (au moins, j'ai un tel comportement sur le dernier chrome).
m03geek
J'avais besoin de le faire dans une boucle gourmande, j'ai comparé cette fonction à celle que j'utilisais jusqu'ici _.merge (objet, [sources]) de lodash (une sorte de lib de soulignement): votre fonction est presque deux fois plus rapide Merci mon pote.
Arnaud Bouchot
20

Object.assign ()

ECMAScript 2015 (ES6)

Il s'agit d'une nouvelle technologie, faisant partie de la norme ECMAScript 2015 (ES6). La spécification de cette technologie a été finalisée, mais vérifiez le tableau de compatibilité pour l'état d'utilisation et d'implémentation dans divers navigateurs.

La méthode Object.assign () est utilisée pour copier les valeurs de toutes les propriétés propres énumérables d'un ou plusieurs objets source vers un objet cible. Il renverra l'objet cible.

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.
Reza Roshan
la source
19

Pour les objets pas trop compliqués, vous pouvez utiliser 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_/

Rappelez-vous que dans cet exemple, "} {" ne doit pas apparaître dans une chaîne!

Algy
la source
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
ou en ligne individuelle:objMerge = JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)).replace(/\}\{/, ", "));
Rabbi Shuki Gur
1
@Charles, désolé mais comment "cette méthode est-elle très dangereuse" mais aussi amusante - c'est la plus sûre possible. D'abord, elle utilise JSON pour faire le gros du travail de lecture du contenu de l'objet, d'autre part, elle utilise l'objet JSON qui est certainement le plus sûr. optimisé pour la récupération de JSO à partir d'un littéral de chaîne d'objet compatible JSON. Et est également rétrocompatible avec IE8 qui est le premier navigateur à implémenter sa norme. Cela me fait juste me demander "pourquoi oh pourquoi avez-vous dû dire une chose aussi stupide et vous en tirer"?
Bekim Bacaj
Je crois que les fonctions de l'objet se décomposeront ici car stringify ne peut pas être utilisé, mais pouvons-nous clarifier d'autres inconvénients?
yeahdixon
18

Il y a une bibliothèque appelée deepmergesur GitHub : Cela semble obtenir une certaine traction. Il s'agit d'un logiciel autonome, disponible via les gestionnaires de packages npm et bower.

Je serais enclin à utiliser ou à améliorer cela au lieu de copier-coller le code des réponses.

Peter Mortensen
la source
17

La meilleure façon de procéder consiste à ajouter une propriété appropriée non énumérable à l'aide d'Object.defineProperty.

De cette façon, vous pourrez toujours parcourir les propriétés de vos objets sans avoir la "nouvelle extension" que vous obtiendriez si vous deviez créer la propriété avec Object.prototype.extend.

Espérons que cela aide:

Object.defineProperty (Object.prototype, "extend", {
    énumérable: faux,
    valeur: fonction (de) {
        var props = Object.getOwnPropertyNames (from);
        var dest = this;
        props.forEach (fonction (nom) {
            if (nom dans dest) {
                var destination = Object.getOwnPropertyDescriptor (de, nom);
                Object.defineProperty (dest, nom, destination);
            }
        });
        retourner ceci;
    }
});

Une fois que cela fonctionne, vous pouvez faire:

var obj = {
    nom: «pile»,
    finition: «débordement»
}
remplacement var = {
    nom: «stock»
};

obj.extend (remplacement);

Je viens d'écrire un article de blog à ce sujet ici: http://onemoredigit.com/post/1527191998/extending-objects-in-node-js

David Coallier
la source
Attendez une seconde --- le code ne fonctionne pas! :( inséré dans jsperf pour le comparer à d'autres algorithmes de fusion, et le code échoue - l'extension de fonction n'existe pas
Jens Roland
16

Vous pouvez simplement utiliser jQuery extend

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

jQuery.extend(obj1, obj2);

obj1Contient maintenant toutes les valeurs de obj1etobj2

Dinusha
la source
2
obj1 contiendra toutes les valeurs, pas obj2. Vous étendez le premier objet. jQuery.extend( target [, object1 ] [, objectN ] )
ruuter
5
Cette question ne concerne pas jQuery. JavaScript! = JQuery
Jorge Riv
1
J'ai cherché sur JavaScript, mais c'est une approche plus simple
Ehsan
13

Le prototype a ceci:

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

obj1.extend(obj2) fera ce que vous voulez.

éphémère
la source
6
Vous vouliez dire Object.prototype.extend? Dans tous les cas, ce n'est pas une bonne idée d'étendre Object.prototype. -1.
SolutionYogi
À bien y penser, il devrait probablement l'être Object.prototypeet non Object... Bonne idée ou non, c'est ce que fait le prototype.jsframework, et donc si vous l'utilisez ou un autre framework qui en dépend, Object.prototypesera déjà étendu avec extend.
éphémère
2
Je ne veux pas vous harceler mais dire que ça va parce que prototype.js le fait est un mauvais argument. Prototype.js est populaire, mais cela ne signifie pas qu'ils sont le modèle sur la façon de faire JavaScript. Consultez cette revue détaillée de la bibliothèque de prototypes par un pro de JS. dhtmlkitchen.com/?category=/JavaScript/&date=2008/06/17/…
SolutionYogi
7
Qui s'en soucie si c'est bien ou mal? Si cela fonctionne, cela fonctionne. Attendre la solution parfaite est génial, sauf lorsque vous essayez de conserver un contrat, de gagner de nouveaux clients ou de respecter les délais. Donnez à tous ceux qui sont passionnés par la programmation de l'argent gratuit et ils finiront par tout faire "correctement".
David
1
Où avez-vous trouvé tous les gars Object.prototype? Object.extendn'interfère pas avec vos objets, il attache simplement une fonction à un Objectobjet. Et obj1.extend(obj2)n'est pas la bonne façon de tirer la fonction, utilisez Object.extend(obj1, obj2)insted
artnikpro
13

** La fusion d'objets est simple à utiliser Object.assignou à l' ...opérateur d' étalement **

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

Les objets sont fusionnés de droite à gauche, cela signifie que les objets qui ont des propriétés identiques à celles des objets à droite seront remplacés.

Dans cet exemple, obj2.carremplaceobj1.car

Legends
la source
10

J'ai étendu la méthode de David Coallier:

  • Ajout de la possibilité de fusionner plusieurs objets
  • Prend en charge les objets profonds
  • remplacer le paramètre (qui est détecté si le dernier paramètre est un booléen)

Si la substitution est fausse, aucune propriété n'est remplacée mais de nouvelles propriétés seront ajoutées.

Utilisation: obj.merge (fusionne ... [, override]);

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

Exemples et cas de 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]));

Ma méthode d'égalité peut être trouvée ici: Comparaison d'objets en JavaScript

gossi
la source
9

Dans Ext JS 4, cela peut être fait comme suit:

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

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

Voir merge (object): Object .

Mark
la source
1
Méfiez-vous du bogue EXTJS-9173 dans EXT.Object.merge pour l'instant après +2 ans non encore résolu
Chris
9

Wow .. c'est le premier article StackOverflow que j'ai vu avec plusieurs pages. Toutes mes excuses pour avoir ajouté une autre "réponse"

Cette méthode s'applique à ES5 et aux versions antérieures - il existe de nombreuses autres réponses concernant ES6.

Je n'ai vu aucun objet "profond" fusionner en utilisant la argumentspropriété. Voici ma réponse - compacte et récursive , permettant de passer des arguments d'objet illimités:

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 partie commentée est facultative. Elle sautera simplement les arguments passés qui ne sont pas des objets (empêchant les erreurs).

Exemple:

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

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

Cette réponse n'est maintenant qu'une goutte dans l'océan ...

Logan
la source
La réponse la plus courte qui fonctionne très bien! Il mérite plus de votes positifs!
Jens Törnell
8
var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }

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

Utilisation de jQuery.extend () - Lien

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

Utilisation de _.merge () - Lien

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

Utilisation de _.extend () - Lien

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

Utilisation d'Object.assign () ECMAScript 2015 (ES6) - Lien

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

Sortie de tous

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"}
Tính Ngô Quang
la source
7

Basé sur la réponse de Markus et vsync , il s'agit d'une version étendue. La fonction prend n'importe quel nombre d'arguments. Il peut être utilisé pour définir des propriétés sur les nœuds DOM et faire des copies complètes des valeurs. Cependant, le premier argument est donné par référence.

Pour détecter un nœud DOM, la fonction isDOMNode () est utilisée (voir la question Stack Overflow JavaScript isDOM - Comment vérifier si un objet JavaScript est un objet DOM? )

Il a été testé dans Opera 11, Firefox 6, Internet Explorer 8 et Google Chrome 16.

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

Quelques exemples

Définir innerHTML et le style d'un élément HTML

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

Fusionner des tableaux et des objets. Notez que non défini peut être utilisé pour conserver les valeurs dans le tableau / objet de gauche.

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

Tout argument ne correspondant pas à un objet JavaScript (y compris null) sera ignoré. À l'exception du premier argument, les nœuds DOM sont également ignorés. Attention, c'est-à-dire que les chaînes, créées comme de nouvelles String () sont en fait des objets.

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

Si vous voulez fusionner deux objets dans un nouveau (sans affecter aucun des deux), fournissez {} comme premier 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

Modifier (par ReaperSoon):

Pour fusionner également des tableaux

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;
}
Snoozer Man
la source
6

let obj1 = {a:1, b:2};
let obj2 = {c:3, d:4};
let merged = {...obj1, ...obj2};
console.log(merged);

Tejas Savaliya
la source
5

Vous devez utiliser les valeurs par défaut de lodash

_.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } });
// → { 'user': { 'name': 'barney', 'age': 36 } }
Raphaël
la source
5

Avec Underscore.js , pour fusionner un tableau d'objets, procédez comme suit:

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

Il en résulte:

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