web-dev-qa-db-fra.com

Lodash - différence entre .extend () / .assign () et .merge ()

Dans la bibliothèque Lodash , quelqu'un peut-il fournir une meilleure explication de fusion et extend/assign .

C'est une question simple mais la réponse m'échappe néanmoins.

443
JDillon522

Voici comment extend/assign fonctionne: Pour chaque propriété de la source, copiez sa valeur telle quelle dans la destination. Si les valeurs de propriété elles-mêmes sont des objets, il n'y a pas de parcours récursif de leurs propriétés. L'objet entier serait pris à partir de la source et défini dans la destination.

Voici comment merge fonctionne: Pour chaque propriété de la source, vérifiez si cette propriété est elle-même un objet. Si c'est le cas, descendez récursivement et essayez de mapper les propriétés de l'objet enfant de la source à la destination. Nous fusionnons donc essentiellement la hiérarchie des objets de la source à la destination. Alors que pour extend/assign, il s’agit d’une simple copie à un niveau des propriétés de la source à la destination.

Voici un simple JSBin qui rendrait cela très clair: http://jsbin.com/uXaqIMa/2/edit?js,console

Voici une version plus élaborée incluant le tableau dans l'exemple également: http://jsbin.com/uXaqIMa/1/edit?js,console

555
Shital Shah

Version Lodash .10.1

Méthodes comparées

  • _.merge(object, [sources], [customizer], [thisArg])
  • _.assign(object, [sources], [customizer], [thisArg])
  • _.extend(object, [sources], [customizer], [thisArg])
  • _.defaults(object, [sources])
  • _.defaultsDeep(object, [sources])

Similitudes

  • Aucun d’entre eux ne fonctionne sur des tableaux comme on peut s’y attendre
  • _.extend est un alias pour _.assign, donc ils sont identiques
  • Tous semblent modifier l'objet cible (premier argument)
  • Tous gèrent null le même

Différences

  • _.defaults et _.defaultsDeep traite les arguments dans l'ordre inverse des autres (bien que le premier argument soit toujours l'objet cible)
  • _.merge et _.defaultsDeep fusionnent des objets enfants et les autres écrasent au niveau racine.
  • Seuls _.assign et _.extend écraseront une valeur avec undefined

Des tests

Ils traitent tous les membres à la racine de manière similaire.

_.assign      ({}, { a: 'a' }, { a: 'bb' }) // => { a: "bb" }
_.merge       ({}, { a: 'a' }, { a: 'bb' }) // => { a: "bb" }
_.defaults    ({}, { a: 'a' }, { a: 'bb' }) // => { a: "a"  }
_.defaultsDeep({}, { a: 'a' }, { a: 'bb' }) // => { a: "a"  }

_.assign gère undefined mais les autres l'ignoreront

_.assign      ({}, { a: 'a'  }, { a: undefined }) // => { a: undefined }
_.merge       ({}, { a: 'a'  }, { a: undefined }) // => { a: "a" }
_.defaults    ({}, { a: undefined }, { a: 'bb' }) // => { a: "bb" }
_.defaultsDeep({}, { a: undefined }, { a: 'bb' }) // => { a: "bb" }

Ils gèrent tous null le même

_.assign      ({}, { a: 'a'  }, { a: null }) // => { a: null }
_.merge       ({}, { a: 'a'  }, { a: null }) // => { a: null }
_.defaults    ({}, { a: null }, { a: 'bb' }) // => { a: null }
_.defaultsDeep({}, { a: null }, { a: 'bb' }) // => { a: null }

Mais seuls _.merge et _.defaultsDeep fusionneront des objets enfants

_.assign      ({}, {a:{a:'a'}}, {a:{b:'bb'}}) // => { "a": { "b": "bb" }}
_.merge       ({}, {a:{a:'a'}}, {a:{b:'bb'}}) // => { "a": { "a": "a", "b": "bb" }}
_.defaults    ({}, {a:{a:'a'}}, {a:{b:'bb'}}) // => { "a": { "a": "a" }}
_.defaultsDeep({}, {a:{a:'a'}}, {a:{b:'bb'}}) // => { "a": { "a": "a", "b": "bb" }}

Et aucun d'entre eux ne fusionnera les tableaux, il semble

_.assign      ({}, {a:['a']}, {a:['bb']}) // => { "a": [ "bb" ] }
_.merge       ({}, {a:['a']}, {a:['bb']}) // => { "a": [ "bb" ] }
_.defaults    ({}, {a:['a']}, {a:['bb']}) // => { "a": [ "a"  ] }
_.defaultsDeep({}, {a:['a']}, {a:['bb']}) // => { "a": [ "a"  ] }

Tous modifient l'objet cible

a={a:'a'}; _.assign      (a, {b:'bb'}); // a => { a: "a", b: "bb" }
a={a:'a'}; _.merge       (a, {b:'bb'}); // a => { a: "a", b: "bb" }
a={a:'a'}; _.defaults    (a, {b:'bb'}); // a => { a: "a", b: "bb" }
a={a:'a'}; _.defaultsDeep(a, {b:'bb'}); // a => { a: "a", b: "bb" }

Aucun ne fonctionne vraiment comme prévu sur les tableaux

Remarque: Comme @Mistic l'a souligné, Lodash considère les tableaux comme des objets dont les clés sont l'index du tableau.

_.assign      ([], ['a'], ['bb']) // => [ "bb" ]
_.merge       ([], ['a'], ['bb']) // => [ "bb" ]
_.defaults    ([], ['a'], ['bb']) // => [ "a"  ]
_.defaultsDeep([], ['a'], ['bb']) // => [ "a"  ]

_.assign      ([], ['a','b'], ['bb']) // => [ "bb", "b" ]
_.merge       ([], ['a','b'], ['bb']) // => [ "bb", "b" ]
_.defaults    ([], ['a','b'], ['bb']) // => [ "a", "b"  ]
_.defaultsDeep([], ['a','b'], ['bb']) // => [ "a", "b"  ]
490
Nate

Une autre différence à prendre en compte est le traitement des valeurs undefined:

mergeInto = { a: 1}
toMerge = {a : undefined, b:undefined}
lodash.extend({}, mergeInto, toMerge) // => {a: undefined, b:undefined}
lodash.merge({}, mergeInto, toMerge)  // => {a: 1, b:undefined}

Ainsi, merge ne fusionnera pas les valeurs de undefined en valeurs définies.

74
samz

Il pourrait également être utile de considérer ce qu’ils font d’un point de vue sémantique:

_.attribuer

   will assign the values of the properties of its second parameter and so on,
   as properties with the same name of the first parameter. (shallow copy & override)

_.fusionner

   merge is like assign but does not assign objects but replicates them instead.
  (deep copy)

_.defaults

   provides default values for missing values.
   so will assign only values for keys that do not exist yet in the source.

_.defaultsDeep

   works like _defaults but like merge will not simply copy objects
   and will use recursion instead.

Je pense qu'apprendre à penser à ces méthodes du point de vue sémantique vous permettrait de mieux "deviner" quel serait le comportement pour tous les différents scénarios de valeurs existantes et non existantes.

20
epeleg

Si vous voulez une copie en profondeur sans substitution tout en conservant la même référence obj

obj = _.assign(obj, _.merge(obj, [source]))

1
mbao01