Lodash - differenza tra .extend () / .assign () e .merge ()


Risposte:


584

Ecco come extend/ assignfunziona: per ogni proprietà nell'origine, copia il suo valore così com'è nella destinazione. se i valori delle proprietà stessi sono oggetti, non c'è attraversamento ricorsivo delle loro proprietà. L'intero oggetto verrebbe prelevato dall'origine e impostato sulla destinazione.

Ecco come mergefunziona: per ogni proprietà nell'origine, controlla se quella proprietà è l'oggetto stesso. In tal caso, riduci in modo ricorsivo e prova a mappare le proprietà dell'oggetto figlio dall'origine alla destinazione. Quindi essenzialmente fondiamo la gerarchia di oggetti dall'origine alla destinazione. Mentre per extend/ assign, è semplice copia di un livello delle proprietà dall'origine alla destinazione.

Ecco JSBin semplice che chiarirebbe questo aspetto: http://jsbin.com/uXaqIMa/2/edit?js,console

Ecco la versione più elaborata che include anche l'array nell'esempio: http://jsbin.com/uXaqIMa/1/edit?js,console


16
Una differenza importante sembra essere che mentre _.merge restituisce un nuovo oggetto unito, _.extend muta l'oggetto di destinazione sul posto,
letronje,

70
Entrambi sembrano mutare l'oggetto di destinazione indipendentemente da ciò che restituiscono.
Jason Rice,

7
Sembra anche che _.extend blocchi i membri dell'oggetto di destinazione se non sono presenti nell'oggetto di origine, il che mi sorprende.
Jason Rice,

5
@JasonRice Non si ostruiscono. Ad esempio in questo violino, la proprietà "a" non viene ostruita . È vero che dopo l'estensione, dest ["p"] ["y"] non esiste più - Questo perché prima che ext src e dest avessero entrambi una proprietà "p", la proprietà "p" di dest viene completamente sovrascritta dalla proprietà "p" di src (ora sono esattamente lo stesso oggetto).
Kevin Wheeler,

14
Per essere chiari, entrambi i metodi modificano / sovrascrivono il primo argomento come riferimento. Quindi, se si desidera un nuovo oggetto dall'unione risultante, è meglio passare letteralmente un oggetto. var combined = merge({}, src, dest)
Jon Jaques,

535

Versione di Lodash 3.10.1

Metodi a confronto

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

Somiglianze

  • Nessuno di loro lavora su array come ci si potrebbe aspettare
  • _.extendè un alias per _.assign, quindi sono identici
  • Tutti sembrano modificare l'oggetto target (primo argomento)
  • Tutti gestiscono nulllo stesso

differenze

  • _.defaultsed _.defaultsDeepelabora gli argomenti in ordine inverso rispetto agli altri (sebbene il primo argomento sia ancora l'oggetto target)
  • _.mergee _.defaultsDeepuniranno gli oggetti figlio e gli altri sovrascriveranno a livello di root
  • Solo _.assigne _.extendsovrascriverà un valore conundefined

test

Tutti gestiscono i membri alla radice in modi simili.

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

_.assigngestisce undefinedma gli altri lo salteranno

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

Gestiscono tutti nulllo stesso

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

Ma solo _.mergee _.defaultsDeepunirà gli oggetti figlio

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

E nessuno di loro unirà array a quanto pare

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

Tutti modificano l'oggetto di destinazione

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

Nessuno funziona davvero come previsto sugli array

Nota: come sottolineato da @Mistic, Lodash considera gli array come oggetti in cui le chiavi sono l'indice dell'array.

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

32
In realtà unisce le matrici esattamente come unisce gli oggetti, poiché le matrici sono oggetti con tasti numerici. Sono d'accordo che ci si aspetterebbe di concatenare o sostituire le matrici, dipende dall'uso.
Mistic,

11
Risposta eccellente. I test furono molto didattici :-)
Lucio Paiva,

5
_.extend is an alias for _.assign, so they are identicalè in conflitto conOnly _.assign will overwrite a value with undefined
Chazt3n il

9
A partire dalla v4.0, _.extend è ora un alias per _.assignIn, non _assign. La funzione assegnatoIn aggiunge la gestione delle proprietà ereditate.
Mike Hedman,

2
null è trattato come non definito qui?
C_B,

75

Un'altra differenza a cui prestare attenzione è la gestione dei undefinedvalori:

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

Quindi mergenon unirà i undefinedvalori in valori definiti.


3
Sono solo io o ciò che rende lodash. Estendersi completamente inutile in quanto restituisce sempre un clone dell'oggetto 'toMerge'?
Jason Rice,

6
Se mergeIntoavesse proprietà che toMergenon avevano, avrebbe mantenuto quelle proprietà. In tal caso non sarebbe un clone.
David Neale,

1
@JasonRice rimuove il vuoto {} e lo unirà in posizione lodash.merge (mergeInto, toMerge)
sidonaldson

20

Potrebbe anche essere utile considerare cosa fanno da un punto di vista semantico:

_.assegnare

   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)

_.merge

   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.

Credo che imparare a pensare a quei metodi dal punto di vista semantico ti permetterebbe di "indovinare" meglio quale sarebbe il comportamento per tutti i diversi scenari di valori esistenti e non esistenti.


3

Se si desidera una copia profonda senza override mantenendo lo stesso objriferimento

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.