Lodash-.extend()/ .assign()和.merge()之间的区别


Answers:


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 2015年

69
无论它们返回什么,它们都似乎使目标对象发生变异。
杰森·赖斯

7
如果源对象中不存在_.extend clobbers成员,这似乎也使我感到惊讶。
杰森·赖斯

5
@JasonRice他们不会被破坏。例如,在这个小提琴中,“ a”属性不会被破坏。的确,在扩展之后,dest [“ p”] [“ y”]不再存在-这是因为在扩展src和dest都具有“ p”属性之前,所以dest的“ p”属性被完全覆盖了通过src的“ p”属性(它们现在是完全相同的对象)。
凯文·惠勒2015年

14
为了清楚起见,这两种方法都通过引用修改/覆盖第一个参数。因此,如果您希望从合并中获得一个新对象,则最好传递一个对象文字。var combined = merge({}, src, dest)
乔恩·雅克

534

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      ({}, { 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"  ]

31
实际上,它完全像合并对象一样合并数组,因为数组是带有数字键的对象。我同意,人们希望根据使用情况来连接或替换数组。
Mistic 2015年

11
极好的答案。测试非常有说服力:-)
Lucio Paiva

5
_.extend is an alias for _.assign, so they are identicalOnly _.assign will overwrite a value with undefined
Chazt3n 2016年

9
从v4.0开始,_.extend现在是_.assignIn的别名,而不是_assign。AssignIn函数增加了对继承属性的处理。
迈克·赫德曼

2
null是否在这里与未定义一样?
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不具有的属性,则它将保留这些属性。在那种情况下,它不是克隆。
David Neale

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]))

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.