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