Mode natif pour fusionner des objets en Javascript

L'objet Javascript ne comporte aucune opération de fusion native. Si vous avez deux objets, disons

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

Et veulent obtenir

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

Pour autant que je sache, il faut faire itinéraire à travers les objets. C'est-à-dire que vous décidez d'une stratégie de fusion ou de fusion et que vous faites quelque chose comme (simplifié)

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

C'est bon. Cependant, Javascript a la fonction d' call et de prototype . Par exemple, transformer des arguments en une Array peut être fait avec

Array.prototype.slice.call(arguments)

Cette approche exploite le code natif existant et donc donc moins susceptible à la folie du programmateur et devrait fonctionner plus rapidement qu'une implémentation non native.

La question

Existe-t-il une astuce pour utiliser ce prototype / motif d'appel sur peut-être les fonctions de traversée Attribute ou Node du DOM, ou peut-être certaines des fonctions génériques de la String afin de réaliser une fusion native d'objet?

Le code ressemblerait à ceci:

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

Et, par conséquent, vous obtenez une fusion native sans parcours.

Une solution possible, sous-optimale

Si vous pouviez utiliser la propriété constructor d'un Object , puis forcer un objet à avoir un constructeur d'un autre objet, puis exécuter de new sur 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 dans javascript pour faire cet appel.

Lemme

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

Vous pouvez les concourir ensemble, puis faire une sorte, puis faire une traversée, sûrement. Mais il serait bien s'il y ait un intersection générique caché quelque part que nous pouvons contraindre un tableau à faire nativement.

Des pensées?

modifier:

Arriver à mi-chemin

Pour le problème de tableau, vous pouvez effectuer les opérations suivantes:

Array.concat (a, b, c) .sort (). Rejoignez (':'), puis utilisez des RegExp capture et de répétition RegExp difficiles pour traverser. Les implémentations de RegExp, si vous ne le savez pas, s'exécutent sur une machine virtuelle simple et simple. 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). Ensuite, le natif parcourt la chaîne d'une manière extrêmement rapide. Peut-être que vous pourriez exploiter cela pour les seuils d'adhésion et obtenir de meilleures performances …

Il n'en reste pas moins.

Ma réponse à cela sera décevante, mais toujours:

non

La raison en est simple: l'implémentation de fusion de Mr Resig (ou "extension" comme appelée pour les objets) dans jQuery fait une boucle, tout comme celle de votre question. Vous pouvez l'examiner ici . Et je dirais que si John Resig n'a pas trouvé une manière intelligente de construire, les simples mortels de stackoverflow ne seront pas non plus 🙂

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

En outre, dans la solution loop et la solution Object.setPrototypeOf() , il vous reste une situation dans laquelle l'objet "propriété override" peut muter l'objet "propriétés par défaut":

 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ération de tiers. Vous accédez maintenant au contrôle de votre objet par défaut et de tout ce qui est référencé dans le lib. Tiers.

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

HTH

Pas que je sache, non. Aussi, 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; } 

Vous pouvez faire ce qui suit en utilisant JS 1.7 natif, sans avoir besoin d'un cadre. Voir l'exemple sur le violon (exemple uniquement destiné aux objets simples – pas d'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); 

En utilisant ES6 (ES2015), vous pouvez utiliser la méthode Object.assign :

 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 l' opérateur de propagation d'objet :

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

Aucun mode natif 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; } 

Ci-dessous, j'ai inclus une fonction de fusion profonde que j'ai écrite. Il ne fusionnera pas profondément les Arrays, seuls les Objets. Il faudra deux objets et renvoie un troisième objet nouveau.

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