Lodash: diferencia entre .extend () / .assign () y .merge ()

Respuestas:

584

Aquí se explica cómo extend/ assignfunciona: para cada propiedad en el origen, copie su valor tal cual al destino. Si los valores de propiedad en sí mismos son objetos, no existe un recorrido recursivo de sus propiedades. Todo el objeto se tomaría del origen y se establecería en el destino.

Así es como mergefunciona: para cada propiedad en la fuente, verifique si esa propiedad es el objeto mismo. Si es así, baje de forma recursiva e intente asignar propiedades de objetos secundarios desde el origen al destino. Entonces, esencialmente fusionamos la jerarquía de objetos de origen a destino. Mientras que para extend/ assign, es una copia simple de un nivel de propiedades desde el origen hasta el destino.

Aquí hay un JSBin simple que aclararía esto: http://jsbin.com/uXaqIMa/2/edit?js,console

Aquí hay una versión más elaborada que incluye una matriz en el ejemplo también: http://jsbin.com/uXaqIMa/1/edit?js,console

Shital Shah
fuente
16
Una diferencia importante parece ser que mientras _.merge devuelve un nuevo objeto fusionado, _.extend muta el objeto de destino en su lugar,
letronje
70
Ambos parecen mutar el objeto de destino independientemente de lo que devuelvan.
Jason Rice
77
También parece que _.extender los miembros de clobbers del objeto de destino si no están presentes en el objeto de origen, lo que me sorprende.
Jason Rice
55
@JasonRice No se golpean. Por ejemplo, en este violín, la propiedad "a" no se destruye . Es cierto que después de la extensión, dest ["p"] ["y"] ya no existe. Esto se debe a que antes de extender src y dest tenían una propiedad "p", por lo que la propiedad "p" de dest se sobrescribe por completo. por la propiedad "p" de src (ahora son exactamente el mismo objeto).
Kevin Wheeler
14
Para ser claros, ambos métodos modifican / sobrescriben el primer argumento por referencia. Entonces, si desea un nuevo objeto de la fusión resultante, lo mejor es pasar un objeto literal. var combined = merge({}, src, dest)
Jon Jaques
535

Lodash versión 3.10.1

Métodos comparados

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

Similitudes

  • Ninguno de ellos funciona en matrices como cabría esperar.
  • _.extend es un alias para _.assign , lo que son idénticos
  • Todos parecen modificar el objeto de destino (primer argumento)
  • Todos ellos manejan nulllo mismo

Las diferencias

  • _.defaultsy _.defaultsDeepprocesa los argumentos en orden inverso en comparación con los demás (aunque el primer argumento sigue siendo el objeto de destino)
  • _.merge y _.defaultsDeep fusionará objetos secundarios y los demás se sobrescribirán en el nivel raíz
  • Solo _.assigny _.extendsobrescribirá un valor conundefined

Pruebas

Todos manejan miembros en la raíz de manera similar.

_.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"  }

_.assignmaneja undefinedpero los demás lo omitirán

_.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" }

Todos manejan nulllo mismo

_.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 }

Pero solo _.mergey _.defaultsDeepfusionará objetos secundarios

_.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" }}

Y ninguno de ellos fusionará matrices, parece

_.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"  ] }

Todos modifican el objeto de destino

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

Ninguno realmente funciona como se esperaba en las matrices

Nota: Como señaló @Mistic, Lodash trata las matrices como objetos donde las claves son el índice de la matriz.

_.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"  ]
Nate
fuente
32
En realidad, combina matrices exactamente como fusiona objetos, porque las matrices son objetos con claves numéricas. Estoy de acuerdo en que uno esperaría concatenar, o reemplazar matrices, depende del uso.
Mistic
11
Excelente respuesta Las pruebas fueron muy didácticas :-)
Lucio Paiva
55
_.extend is an alias for _.assign, so they are identicalconflictos conOnly _.assign will overwrite a value with undefined
Chazt3n
99
A partir de v4.0, _.extend ahora es un alias para _.assignIn, no _assign. La función asignarIn agrega que trata con propiedades heredadas.
Mike Hedman
2
¿se trata nulo de la misma manera que aquí?
C_B
75

Otra diferencia a la que prestar atención es el manejo de undefinedvalores:

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

Por mergelo tanto , no fusionará undefinedvalores en valores definidos.

samz
fuente
3
¿Soy solo yo o eso hace que lodash.extend sea completamente inútil ya que siempre devuelve un clon del objeto 'toMerge'?
Jason Rice
66
Si mergeIntotuviera propiedades que toMergeno tuvieran, entonces retendría esas propiedades. En ese caso no sería un clon.
David Neale
1
@JasonRice elimina el {} vacío y lo fusionará en su lugar lodash.merge (mergeInto, toMerge)
sidonaldson
20

También podría ser útil considerar lo que hacen desde un punto de vista semántico:

_.asignar

   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)

_.unir

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

_defectos

   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.

Creo que aprender a pensar en esos métodos desde el punto de vista semántico le permitiría "adivinar" mejor cuál sería el comportamiento para todos los diferentes escenarios de valores existentes y no existentes.

epeleg
fuente
3

Si desea una copia profunda sin anulación mientras conserva la misma objreferencia

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

mbao01
fuente