Manière Native de fusionner des objets en Javascript

L'objet Javascript N'a aucune opération de fusion native. Si vous avez deux objets, dites

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

Et que vous voulez obtenir

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

Pour autant que je sache, vous devez parcourir les objets. C'est-à-dire que vous décidez soit d'une stratégie de fusion à gauche ou à droite, puis vous faites quelque chose comme (simplifié)

for (key in object2) {
  object1[key] = object2[key];
}

C'est très bien. Cependant, Javascript a la fonctionnalité call et prototype. Par exemple, transformer arguments en Array peut être fait avec

Array.prototype.slice.call(arguments)

Cette approche exploite le code natif existant, et est donc moins sensible à la folie du programmeur et devrait s'exécuter plus rapidement qu'une implémentation non native.

La question

Y a-t-il une astuce pour utiliser ce modèle prototype/appel sur peut-être les caractéristiques de traversée Attribute ou Node du DOM, ou peut-être certaines des fonctions génériques String afin de faire une fusion d'objet natif?

Le code ressemblerait à quelque chose comme ce:

var merged = somethingrandom.obscuremethod.call(object1, object2)

Et par conséquent, vous obtiendriez une fusion native sans traversée.

Une solution possible, sous-optimale

Si vous pouviez utiliser le constructor propriété d'un Object, puis forcer un objet à posséder un constructeur d'un autre objet, puis exécutez new plus de l'objet composite, vous pouvez obtenir une fusion gratuitement. Mais je n'ai pas une compréhension ferme des implications complètes de la fonctionnalité constructor en javascript pour le faire appeler.

Lemme

La même question vaut pour Arrays. Un problème commun est d'être, par exemple 7 tableaux de nombres, puis essayer de trouver l'intersection de ces tableaux. C'est-à-dire, quels nombres existent dans les 7 tableaux.

Vous pourriez les concat entre eux, puis faire une sorte, puis faire une traversée, sûrement. Mais ce serait bien s'il y a un intersect Générique caché quelque part que nous pouvons forcer un tableau à faire nativement.

Tout pensées?

Modifier:

À mi-chemin

Pour le problème du tableau, vous pouvez faire ce qui suit:

Tableau.concat(a, b, c).sorte().join (':'), puis utilisez des modèles de capture et de répétition RegExp difficiles à parcourir. Les implémentations RegExp, si vous ne le savez pas, s'exécutent sur une machine virtuelle très simple basée sur une pile. Lorsque vous initialisez votre expression régulière, c'est vraiment un programme qui est compilé (RegExp.compile est une méthode js obsolète). Puis le native court sur la chaîne d'une manière extrêmement rapide. Peut-être pourriez-vous exploiter cela pour les seuils d'adhésion et obtenir de meilleures performances...

Il ne va toujours pas tout le chemin cependant.

47
demandé sur TylerH 2011-01-20 00:10:50

7 réponses

Ma réponse à cela sera décevante, mais quand même:

Pas de

La raison en est simple: l'implémentation de merge par Mr Resig (ou "extend" comme on l'appelle pour les objets) dans jQuery fait une boucle, tout comme celle de votre question. Vous pouvez le regarder ici . Et j'ose dire que si John Resig n'a pas trouvé une manière intelligente de le faire, alors les simples mortels de stackoverflow ne le feront pas non plus:)

24
répondu Jakob 2014-07-01 09:10:58

La question du million de dollars! J'ai essayé de faire cela de nombreuses façons, et la manière de boucle décrite ci-dessus semblait toujours la plus sale. ES6 Object.setPrototypeOf() vous permet de déléguer un objet "propriété override" à un objet "Propriétés par défaut", accomplissant à peu près ce que vous essayez de faire, mais l'utilisation de Object.setPrototypeOf() a des implications sérieuses, comme désactiver les optimisations du compilateur du navigateur pour l'ensemble du script.

En outre, dans la solution de boucle et la solution Object.setPrototypeOf(), il vous reste un situation où l'objet "property override" peut muter l'objet" default properties":

defaultObj = {
    a: [1, 2]
}
...
overrideObj = {
    b: 3
}
Object.setPrototypeOf(overrideObj, defaultObj);
console.log(overrideObj); // {a: [1, 2], b: 3}
// Great!
...
overrideObj.a.push(4);
console.log(defaultObj); // {a: [1, 2, 4]}
// Uh-oh.

Vous pourriez penser que ce n'est pas un problème, mais disons que vous utilisez cet objet comme configuration pour une lib tierce partie. Vous remettez maintenant le contrôle de votre objet par défaut et de tout ce qui y est référencé à la 3rd party lib.

Une meilleure solution pourrait être d'utiliser JSON.stringify et JSON.analyser pour copier et combiner les objets. Voici un essentiel avec le exemple: https://gist.github.com/spikesagal/6f7822466887f19b9c65

HTH

3
répondu Spike Sagal 2015-11-06 17:37:50

Pas que je sache, non. En outre, vous voudrez écrire votre méthode de fusion comme ceci:

function mergeInto(o1, o2) {
  if (o1 == null || o2 == null)
    return o1;

  for (var key in o2)
    if (o2.hasOwnProperty(key))
      o1[key] = o2[key];

  return o1;
}
2
répondu Brian Donovan 2011-01-19 21:24:55

En utilisant ES6 (ES2015), vous pouvez utiliser objet.attribuez Méthode:

var x = {a:1, b:2};
var y = {c:3, d:4};
var z = Object.assign({},x,y);

En utilisant ES7 (ES2016, Chrome 60+ ou Babel) vous pouvez utiliser opérateur de propagation D'objet :

var x = {a:1, b:2};
var y = {c:3, d:4}; 
var z = {...x, ...y};
2
répondu DanSavoy 2017-08-11 18:32:36

Vous pouvez faire ce qui suit en utilisant JS 1.7 natif, sans avoir besoin d'un framework. Voir l'exemple sur fiddle (Exemple uniquement destiné aux objets simples - pas aux objets imbriqués complexes)

var obj1 = {a: "a", b: "b"};
var obj2 = {c: "c", d: "d"};

// The magic: ugly but works perfectly
var value = (JSON.stringify(obj1).concat(JSON.stringify(obj2))).replace("}{", ",");

document.getElementById("lbl1").setAttribute("value", value);

// back to object
var obj3 = JSON.parse(value);
document.getElementById("lbl2").setAttribute("value", obj3.a + " " + obj3.b + " " + obj3.c + " " + obj3.d);
1
répondu Ron Anon 2013-08-15 01:45:34

Pas de moyens natifs dans ECMA-Script, utilisez:

function merge(o1,o2) {
 if (typeof(o1)!=='object') o1={};
 if (typeof(o2)!=='object') o2={};
 for (var k in o2) {
 if (o1[k]!==undefined)
  alert ('Collision Error'); // TODO
 else
   o1[k]=o2[k];
 }
 return o1;
}
0
répondu Informate.it 2014-04-07 07:09:53

Ci-dessous, j'ai inclus une fonction de fusion profonde que j'ai écrite. Il ne fusionnera pas en profondeur les tableaux, seulement les objets. Il faudra deux objets et retournera un troisième nouvel objet.

var merge = function(o1, o2) {
    var o_new = {};
    for(p in o1) {
        if(o1[p]) {
            if(typeof o1[p] == 'object' && !(o1[p] instanceof Array) && o2.hasOwnProperty(p)) {
                o_new[p] = merge(o1[p], o2[p]);
            }
            else {
                o_new[p] = o1[p];
            }
        }
    }
    for(p in o2) {
        if(typeof o2[p] == 'object' && !(o2[p] instanceof Array) && o1.hasOwnProperty(p)) {
            o_new[p] = merge(o1[p], o2[p]);
        }
        else {
            o_new[p] = o2[p];
        }
    }
    return o_new;
}
-2
répondu cpreid 2016-10-20 14:47:46