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

Nella libreria di Lodash , qualcuno può fornire una spiegazione migliore di unire ed estendere / assegnare .

È una domanda semplice ma la risposta mi sfugge comunque.

Ecco come funziona l’ extend / assign : per ogni proprietà in origine, copia il suo valore così com’è per destinazione. se i valori delle proprietà stessi sono oggetti, non c’è attraversamento ricorsivo delle loro proprietà. L’intero object sarebbe preso dalla fonte e impostato a destinazione.

Ecco come funziona l’ merge : per ogni proprietà in origine, controlla se quella proprietà è l’object stesso. Se è quindi andare giù ricorsivamente e provare a mappare le proprietà dell’object figlio dall’origine alla destinazione. Quindi essenzialmente uniamo la gerarchia degli oggetti dall’origine alla destinazione. Mentre per extend / assign , è semplice copia di un livello delle proprietà dall’origine alla destinazione.

Ecco il semplice JSBin che renderebbe questo chiarissimo: http://jsbin.com/uXaqIMa/2/edit?js,console

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

Versione di Lodash 3.10.1

Metodi confrontati

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

Analogie

  • Nessuno di loro lavora sugli array come ci si potrebbe aspettare
  • _.extend è un alias per _.assign , quindi sono identici
  • Tutti sembrano modificare l’object di destinazione (primo argomento)
  • Tutti loro gestiscono null lo stesso

differenze

  • _.defaults e _.defaultsDeep elaborano gli argomenti in ordine inverso rispetto agli altri (sebbene il primo argomento sia ancora l’object di destinazione)
  • _.merge e _.defaultsDeep gli oggetti figlio e gli altri sovrascriveranno a livello di root
  • Solo _.assign e _.extend sovrascriveranno un valore con undefined

test

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

_.assign gestisce undefined ma gli altri lo _.assign

 _.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 null lo 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 _.merge e _.defaultsDeep 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à gli array che sembra

 _.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’object target

 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 tratta gli array come oggetti in cui le chiavi sono l’indice nell’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" ] 

Un’altra differenza da prestare attenzione è la gestione dei valori 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} 

Quindi l’ merge non unirà i valori non undefined in valori definiti.

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 meglio “indovinare” quale sarebbe il comportamento per tutti i diversi scenari di valori esistenti e non esistenti.