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.