Comment fusionner deux objets javascript ensemble dans ES6 +?

141

J'en ai marre de devoir toujours écrire du code comme celui-ci:

function shallowExtend(obj1,obj2){
  var key;
  for ( key in obj2 ) {
    if ( obj2.hasOwnProperty(key) === false )  continue;
    obj1[key] = obj2[key]
  }
}

Ou si je ne veux pas écrire le code moi-même, implémentez une bibliothèque qui le fait déjà. ES6 + vient sûrement à la rescousse, cela nous fournira quelque chose comme un Object.prototype.extend(obj2...)ouObject.extend(obj1,obj2...)

Est-ce que ES6 + fournit une telle fonctionnalité? Si ce n'est déjà fait, une telle fonctionnalité est-elle prévue? Si ce n'est pas prévu, pourquoi pas?

balupton
la source
3
Alors pourquoi ne l'avez-vous pas ajouté à votre bibliothèque?
RobG
12
@RobG, cette question porte sur l'espoir qu'ES6 nous évitera d'avoir besoin d'une telle merde standard pour ce que ça vaut: github.com/balupton/bal-util/blob/…
balupton
Je ne pense pas qu'il existe un moyen général de copier les paires nom / valeur d'un objet à un autre. Vous traitez uniquement avec vos propres propriétés ou celles de la [[Prototype]]chaîne? Faites-vous des copies «profondes» ou «superficielles»? Qu'en est-il des propriétés non énumérables et non inscriptibles? Je pense que je préfère avoir une petite fonction de bibliothèque qui fait ce dont j'ai besoin, et qui est surtout évitable de toute façon.
RobG

Réponses:

206

Vous pourrez effectuer une fusion / extension / affectation superficielle dans ES6 en utilisant Object.assign:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Syntaxe:

Object.assign ( cible , sources );

... sources représente le ou les objets source.

Exemple:

var obj1 = {name: 'Daisy', age: 30};
var obj2 = {name: 'Casey'};

Object.assign(obj1, obj2);

console.log(obj1.name === 'Casey' && obj1.age === 30);
// true
Jack
la source
64
Une petite note: Object.assign mute la cible. Si ce n'est pas ce que vous voulez, vous pouvez l'appeler avec un objet vide:let merged = Object.assign({}, source1, source2);
david
20
Veuillez noter qu'il s'agit d'une extension peu profonde ... les objets imbriqués ne sont PAS fusionnés
monzonj
@monzonj, n'y a-t-il pas d'option pour étendre les objets imbriqués, sans utiliser lodash ou mout?
Joao Falcao
1
Il existe un bon moyen de fusionner des objets profondément imbriqués sans bibliothèque en utilisant simplement Object.assign: voir ma réponse ici
Ruslan
Une ancienne façon d'étendre les objets imbriqués est d'utiliserJSON.parse(JSON.stringify(src))
Andre Figueiredo
162

Vous pouvez utiliser la syntaxe de propagation d'objets pour cela:

const merged = {...obj1, ...obj2}

Pour les tableaux, l'opérateur de propagation faisait déjà partie de ES6 (ES2015), mais pour les objets, il a été ajouté à la spécification du langage à ES9 (ES2018). Sa proposition a été activée par défaut dans des outils comme Babel bien avant cela.

Thijs Koerselman
la source
3
Non officiellement, il s'appelle maintenant ES2015: P Depuis quand la déstructuration ne fait-elle pas partie de ES6? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... Exécution de babel-node: const ob1 = {foo: 123}; const ob2 = {bar: 234}; const merged = {...ob1, ...ob2}; console.log(merged) Sortie:{ foo: 123, bar: 234 }
Thijs Koerselman
9
Ce n'est pas de la déstructuration, cela se propage - et non, pour les objets, cela ne fait pas partie d'ES6. Vous devez désactiver les brouillons expérimentaux ES7 dans votre babel.
Bergi le
2
Ah pardonne-moi. Vous avez raison. C'est une fonctionnalité de Babel stage 2 pour le moment. github.com/sebmarkbage/ecmascript-rest-spread Je ne m'en suis jamais rendu compte car je l'utilise depuis le début avec babel et il est activé par défaut. Mais comme vous devez de toute façon transpiler, et que la propagation de l'objet est une chose assez simple, je le recommanderais quand même. J'aime cela.
Thijs Koerselman le
Ils sont activés par défaut, vraiment? Cela ressemble à un bug.
Bergi le
2
"Les propositions de niveau 2 ou supérieur sont activées par défaut". babeljs.io/docs/usage/experimental
Thijs Koerselman
14

Je sais que c'est un peu un vieux problème, mais la solution la plus simple dans ES2015 / ES6 est en fait assez simple, en utilisant Object.assign (),

Espérons que cela aide, cela permet également de fusionner DEEP :

/**
 * Simple is object check.
 * @param item
 * @returns {boolean}
 */
export function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item) && item !== null);
}

/**
 * Deep merge two objects.
 * @param target
 * @param source
 */
export function mergeDeep(target, source) {
  if (isObject(target) && isObject(source)) {
    for (const key in source) {
      if (isObject(source[key])) {
        if (!target[key]) Object.assign(target, { [key]: {} });
        mergeDeep(target[key], source[key]);
      } else {
        Object.assign(target, { [key]: source[key] });
      }
    }
  }
  return target;
}

Exemple d'utilisation:

mergeDeep(this, { a: { b: { c: 123 } } });
// or
const merged = mergeDeep({a: 1}, { b : { c: { d: { e: 12345}}}});  
console.dir(merged); // { a: 1, b: { c: { d: [Object] } } }
Salakar
la source
7

L'ajout de Object.mixinest actuellement en discussion pour prendre en charge le comportement que vous demandez. https://mail.mozilla.org/pipermail/es-discuss/2012-December/027037.html

Bien que ce ne soit pas encore dans le projet ES6, il semble qu'il y ait beaucoup de soutien pour cela, donc je pense qu'il apparaîtra bientôt dans les projets.

Mur de Nathan
la source
13
.mixina été abandonné par TC39.
Knu
5
Attention - cette réponse n'est plus correcte, voir la réponse de Jack pour une approche correcte et fonctionnelle.
Benjamin Gruenbaum
Object.mixin a été remplacé par Object.assign
gotofritz
7

ES6

Object.assign(o1,o2) ; 
Object.assign({},o1,o2) ; //safe inheritance
var copy=Object.assign({},o1); // clone o1
//------Transform array of objects to one object---
var subjects_assess=[{maths:92},{phy:75},{sport:99}];
Object.assign(...subjects_assess); // {maths:92,phy:75,sport:99}

ES7 ou Babel

{...o1,...o2} // inheritance
 var copy= {...o1};
Abdennour TOUMI
la source
1
@FilipBartuzi ce n'est pas ES6 auquel vous vous êtes lié. Mais il fait essentiellement l'équivalent de _.extend () dans lodash / underscore.
Nostalg.io
5

Peut-être que la Object.definePropertiesméthode ES5 fera l'affaire?

par exemple

var a = {name:'fred'};
var b = {age: {value: 37, writeable: true}};

Object.defineProperties(a, b);

alert(a.age); // 37

Documentation MDN: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperties

RobG
la source
Soyez prudent, cependant. Sur au moins un navigateur, cela a des implications sur les performances.
Reuben Morais
1
Je pense que la partie la plus intéressante est celle qui definePropertiesdéfinit ses propres propriétés. Il n'écrase pas les propriétés de la [[prototype]]chaîne, il les masque.
RobG
2
Bonne suggestion, mais pas vraiment une extension car elle sert plus à définir comment les propriétés doivent se comporter ... Faire un simple Object.defineProperties (obj1, obj2) entraînerait des résultats inattendus.
balupton
aurait à utiliser Object.getOwnPropertyDescriptorégalement pour définir la propriété lorsqu'il s'agit d'une valeur complexe, ou vous copierez par référence.
dmp le