Лодаш - різниця між .extend () / .assign () і .merge ()


Відповіді:


582

Ось як extend/ assignпрацює: Для кожної властивості в джерелі скопіюйте її значення як є у пункт призначення. якщо значення властивостей самі по собі є об'єктами, не відбувається рекурсивного обходу їх властивостей. Весь об'єкт буде взято з джерела та встановлений у пункт призначення.

Ось як mergeпрацює: Для кожної властивості в джерелі перевірте, чи є ця властивість самим об'єктом. Якщо це так, спустіться рекурсивно та спробуйте зіставити властивості дочірнього об'єкта від джерела до місця призначення. Тож по суті ми об'єднуємо ієрархію об'єктів від джерела до місця призначення. Хоча для extend/ assign, це проста однорівнева копія властивостей від джерела до місця призначення.

Ось простий JSBin, який би зробив це кристально чистим: http://jsbin.com/uXaqIMa/2/edit?js,console

Ось більш детальна версія, яка включає також масив у прикладі: http://jsbin.com/uXaqIMa/1/edit?js,console


16
Важлива відмінність полягає в тому, що в той час як _.merge повертає новий об'єднаний об'єкт, _.extend мутує об'єкт призначення на місці,
letronje

69
Вони, як видається, мутують об'єкт призначення незалежно від того, що вони повертають.
Джейсон Райс

7
Також виявляється, що _.extend клобує членів об'єкта призначення, якщо вони відсутні у вихідному об'єкті, що мене пригнічує.
Джейсон Райс

5
@JasonRice Вони не розбиваються. Наприклад, у цій скрипці властивість "a" не має клобування . Це правда, що після розширення dest ["p"] ["y"] більше не існує. Це тому, що раніше, ніж src розширення та dest мали обидві властивості "p", то властивість "p" dest стає повністю перезаписаним властивістю "p" src (зараз вони точно такий же об'єкт).
Кевін Вілер

14
Щоб було зрозуміло, обидва способи змінюють / перезаписують перший аргумент за посиланням. Тож якщо ви хочете новий об'єкт із отриманого злиття, краще передати об'єкт буквально. var combined = merge({}, src, dest)
Джон Жак

534

Версія Лодаша 3.10.1

Методи порівняння

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

Подібність

  • Жоден з них не працює на масивах, як ви могли очікувати
  • _.extendПсевдонім для _.assign, тому вони ідентичні
  • Всі вони, здається, змінюють цільовий об'єкт (перший аргумент)
  • Усі вони справляються nullоднаково

Відмінності

  • _.defaultsта _.defaultsDeepобробляє аргументи у зворотному порядку порівняно з іншими (хоча перший аргумент все ще є цільовим об'єктом)
  • _.mergeі _.defaultsDeepоб'єднає дочірні об’єкти, а інші замінять на рівні коренів
  • Тільки _.assignі _.extendперезапишемо значенняundefined

Тести

Всі вони обробляють членів у корені подібними способами.

_.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ручки, undefinedале інші пропустить це

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

Всі вони справляються nullоднаково

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

Але тільки _.mergeі _.defaultsDeepбудуть зливатися дочірні предмети

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

І жоден з них не злиється масивів, здається

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

Усі змінюють цільовий об’єкт

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

Жоден насправді не працює так, як очікувалося на масивах

Примітка. Як зазначав @Mistic, Лодаш розглядає масиви як об'єкти, де ключі є індексом масиву.

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

31
Він насправді об'єднує масиви точно так, як об'єднує об’єкти, оскільки масиви - це об'єкти з цифровими клавішами. Я погоджуюся, що можна було б сподіватися на об'єднання або заміну масивів, залежно від використання.
Містик

11
Відмінна відповідь. Тести були дуже дидактичними :-)
Лусіо Пайва

5
_.extend is an alias for _.assign, so they are identicalконфлікти зOnly _.assign will overwrite a value with undefined
Chazt3n

9
Станом на v4.0, _.extend тепер є псевдонімом для _.assignIn, а не _assign. Функція dodIn додає справу зі спадковими властивостями.
Майк Хедман

2
чи обробляється з нулем так само, як тут не визначено?
C_B

75

Ще одна різниця, на яку слід звернути увагу, - це обробка 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}

Таким чином merge, undefinedзначення не буде об'єднано у визначені значення.


3
Це лише я чи робить те, що lodash.extend абсолютно марний тим, що він завжди повертає клон об’єкта 'toMerge'?
Джейсон Райс

6
Якщо б mergeIntoбули властивості, яких toMergeне було, вони б зберігали ці властивості. У цьому випадку це не був би клон.
Девід Ніл

1
@JasonRice видаліть порожній {}, і він об'єднає його на місці lodash.merge (mergeInto, toMerge)
sidonaldson

20

Також може бути корисно розглянути, що вони роблять з семантичної точки зору:

_.призначення

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

Я вважаю, що навчання думати про ці методи з семантичної точки зору дозволить вам краще «здогадатися», якою буде поведінка для всіх різних сценаріїв існуючих та неіснуючих цінностей.


3

Якщо ви хочете отримати глибоку копію без зміни, зберігаючи ту саму objпосилання

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.