Lodash - perbedaan antara .extend () / .assign () dan .merge ()

Jawaban:

584

Begini caranya extend/ assignberfungsi: Untuk setiap properti di sumber, salin nilainya apa adanya ke tujuan. jika nilai properti itu sendiri adalah objek, tidak ada traversal rekursif dari properti mereka. Seluruh objek akan diambil dari sumber dan diatur ke tujuan.

Begini caranya merge: Untuk setiap properti dalam sumber, periksa apakah properti itu objek itu sendiri. Jika kemudian turun secara rekursif dan mencoba memetakan properti objek anak dari sumber ke tujuan. Jadi intinya kita menggabungkan hierarki objek dari sumber ke tujuan. Sedangkan untuk extend/ assign, ini adalah salinan properti satu tingkat sederhana dari sumber ke tujuan.

Inilah JSBin sederhana yang akan membuat ini sejernih kristal: http://jsbin.com/uXaqIMa/2/edit?js,console

Berikut adalah versi yang lebih rumit yang mencakup array dalam contoh juga: http://jsbin.com/uXaqIMa/1/edit?js,console

Shital Shah
sumber
16
Perbedaan penting tampaknya adalah bahwa sementara _.merge mengembalikan objek yang digabungkan baru, _.extend memutasikan objek tujuan di tempat,
letronje
70
Mereka berdua tampak bermutasi objek tujuan terlepas dari apa yang mereka kembali
Jason Rice
7
Tampak juga bahwa _.extend anggota kelompok objek wisata jika mereka tidak hadir dalam objek sumber, yang mengejutkan bagi saya.
Jason Rice
5
@JasonRice Mereka tidak akan musnah. Misalnya dalam biola ini, properti "a" tidak musnah . Benar bahwa setelah ext, dest ["p"] ["y"] tidak ada lagi - Ini karena sebelum ext src dan dest keduanya memiliki properti "p", jadi properti "p" dest akan ditimpa sepenuhnya oleh properti "p" src (mereka adalah objek yang sama persis sekarang).
Kevin Wheeler
14
Agar lebih jelas, kedua metode memodifikasi / menimpa argumen pertama dengan referensi. Jadi jika Anda menginginkan objek baru dari gabungan yang dihasilkan, yang terbaik untuk melewatkan objek literal. var combined = merge({}, src, dest)
Jon Jaques
535

Lodash versi 3.10.1

Metode dibandingkan

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

Kesamaan

  • Tak satu pun dari mereka bekerja pada array seperti yang Anda harapkan
  • _.extend adalah alias untuk _.assign , jadi mereka identik
  • Semuanya sepertinya memodifikasi objek target (argumen pertama)
  • Semuanya menangani nullhal yang sama

Perbedaan

  • _.defaults dan _.defaultsDeep memproses argumen dalam urutan terbalik dibandingkan dengan yang lain (meskipun argumen pertama masih menjadi objek target)
  • _.merge dan _.defaultsDeep akan menggabungkan objek anak dan yang lainnya akan menimpa di tingkat root
  • Hanya _.assigndan _.extendakan menimpa nilai denganundefined

Tes

Mereka semua menangani anggota di root dengan cara yang sama.

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

_.assignmenangani undefinedtetapi yang lain akan melewatkannya

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

Mereka semua menangani nullhal yang sama

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

Tetapi hanya _.mergedan _.defaultsDeepakan menggabungkan objek anak

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

Dan sepertinya tidak satupun dari mereka yang menggabungkan array

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

Semua memodifikasi objek 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" }

Tidak ada yang benar-benar berfungsi seperti yang diharapkan pada array

Catatan: Seperti yang ditunjukkan @Mistic, Lodash memperlakukan array sebagai objek di mana kunci adalah indeks ke dalam 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"  ]
Nate
sumber
32
Ini sebenarnya menggabungkan array persis seperti itu menggabungkan objek, karena array adalah objek dengan kunci numerik. Saya setuju bahwa orang akan mengharapkan untuk menggabungkan, atau mengganti array, tergantung pada penggunaan.
Mistic
11
Jawaban yang sangat bagus. Tesnya sangat didaktik :-)
Lucio Paiva
5
_.extend is an alias for _.assign, so they are identicalkonflik denganOnly _.assign will overwrite a value with undefined
Chazt3n
9
Pada v4.0, _.extend sekarang menjadi alias untuk _.assignIn, bukan _assign. Fungsi assignIn menambahkan berurusan dengan properti yang diwarisi.
Mike Hedman
2
apakah null diperlakukan sama dengan yang tidak terdefinisi di sini?
C_B
75

Perbedaan lain yang harus diperhatikan adalah penanganan undefinednilai:

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

Jadi mergetidak akan menggabungkan undefinednilai menjadi nilai yang ditentukan.

samz
sumber
3
Apakah hanya saya atau itu yang membuat lodash. Perluas sama sekali tidak berguna karena selalu mengembalikan klon objek 'toMerge'?
Jason Rice
6
Jika mergeIntomemiliki properti yang toMergetidak dimiliki maka itu akan mempertahankan properti itu. Dalam hal ini itu tidak akan menjadi tiruan.
David Neale
1
@JasonRice menghapus {} yang kosong dan itu akan menggabungkannya di tempat lodash.merge (mergeInto, toMerge)
sidonaldson
20

Mungkin juga bermanfaat untuk mempertimbangkan apa yang mereka lakukan dari sudut pandang semantik:

_.menetapkan

   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)

_.menggabungkan

   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.

_.defaultDep

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

Saya percaya bahwa belajar memikirkan metode-metode tersebut dari sudut pandang semantik akan membuat Anda lebih baik "menebak" apa yang akan menjadi perilaku untuk semua skenario yang berbeda dari nilai yang ada dan yang tidak ada.

epeleg
sumber
3

Jika Anda ingin salinan yang dalam tanpa mengganti sambil mempertahankan objreferensi yang sama

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

mbao01
sumber