Lodash - разница между .extend()/.assign() и .merge()
В библиотеке Lodash кто-то может дать лучшее объяснение merge и продлить/назначить.
Это простой вопрос, но ответ все же уклоняется от меня.
Ответы
Ответ 1
Здесь работает extend
/assign
: для каждого свойства в источнике скопируйте его значение as-is в пункт назначения. если сами значения свойств являются объектами, рекурсивный обход их свойств отсутствует. Весь объект будет взят из источника и установлен в пункт назначения.
Вот как работает merge
: для каждого свойства в источнике проверьте, является ли это свойство самим объектом. Если он затем рекурсивно идет вниз и пытается сопоставить свойства дочерних объектов от источника к месту назначения. Таким образом, мы объединим иерархию объектов от источника к месту назначения. Пока для extend
/assign
, это простая одноуровневая копия свойств из источника в пункт назначения.
Здесь простой JSBin, который сделает этот кристалл ясным:
http://jsbin.com/uXaqIMa/2/edit?js,console
Здесь более сложная версия, которая также включает массив в примере:
http://jsbin.com/uXaqIMa/1/edit?js,console
Ответ 2
Lodash версия 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
его
_.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, Lodash рассматривает массивы как объекты, ключи которых являются индексом в массиве.
_.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" ]
Ответ 3
Еще одно отличие, на которое нужно обратить внимание: обработка значений 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
в определенные значения.
Ответ 4
Также может быть полезно рассмотреть, что они делают с семантической точки зрения:
_.assign
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.
Я считаю, что научиться думать об этих методах с семантической точки зрения позволит вам лучше "угадать", каково поведение всех сценариев существующих и не существующих значений.
Ответ 5
Если вы хотите глубокое копирование без переопределения при сохранении той же ссылки на obj
obj = _.assign(obj, _.merge(obj, [source]))