Lodash - .extend() / .assign() 和 .merge() 之间的区别
Posted
技术标签:
【中文标题】Lodash - .extend() / .assign() 和 .merge() 之间的区别【英文标题】:Lodash - difference between .extend() / .assign() and .merge() 【发布时间】:2013-11-26 18:23:32 【问题描述】:在Lodash 库中,有人可以对merge 和extend / assign 提供更好的解释。
这是一个简单的问题,但答案却让我回避了。
【问题讨论】:
【参考方案1】:如果您想要一个没有覆盖的深层副本,同时保留相同的 obj
参考
obj = _.assign(obj, _.merge(obj, [source]))
【讨论】:
【参考方案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 (, 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" ]
【讨论】:
它实际上合并数组就像它合并对象一样,因为数组是带有数字键的对象。我同意人们期望连接或替换数组,这取决于使用情况。 优秀的答案。测试非常具有指导意义:-)_.extend is an alias for _.assign, so they are identical
与 Only _.assign will overwrite a value with undefined
冲突
从 v4.0 开始,_.extend 现在是 _.assignIn 的别名,而不是 _assign。 assignIn 函数增加了对继承属性的处理。
这里的 null 是否与未定义相同?【参考方案3】:
从语义的角度考虑它们的作用也可能会有所帮助:
_.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.
我相信,学习从语义的角度思考这些方法可以让您更好地“猜测”现有和非现有值的所有不同场景的行为。
【讨论】:
【参考方案4】:extend
/assign
的工作原理如下:对于源中的每个属性,将其值原样复制到目标。如果属性值本身是对象,则不会递归遍历它们的属性。整个对象将从源获取并设置到目标。
merge
的工作原理如下:对于源中的每个属性,检查该属性是否是对象本身。如果是,则递归下去并尝试将子对象属性从源映射到目标。所以本质上我们将对象层次结构从源合并到目标。而对于extend
/assign
,它是从源到目标的简单的一级属性副本。
这里有一个简单的 JSBin,可以让这一切变得一清二楚: http://jsbin.com/uXaqIMa/2/edit?js,console
下面是更详细的版本,示例中也包含数组: http://jsbin.com/uXaqIMa/1/edit?js,console
【讨论】:
一个重要的区别似乎是,当 _.merge 返回一个新的合并对象时,_.extend 就地改变目标对象, 无论返回什么,它们似乎都会改变目标对象。 如果源对象中不存在目标对象的 _.extend clobbers 成员,这让我感到惊讶。 @JasonRice 他们不会受到打击。 For example in this fiddle, the "a" property doesn't get clobbered。确实,在extend之后,dest["p"]["y"]不再存在——这是因为在extend之前src和dest都有一个"p"属性,所以dest的"p"属性被完全覆盖了通过 src 的“p”属性(它们现在是完全相同的对象)。 明确地说,这两种方法都通过引用修改/覆盖了 first 参数。因此,如果您想从结果合并中获得一个新对象,最好传递一个对象文字。var combined = merge(, src, dest)
【参考方案5】:
另一个需要注意的区别是处理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
值合并到定义的值中。
【讨论】:
是我自己还是 lodash.extend 完全没用,因为它总是返回 'toMerge' 对象的克隆? 如果mergeInto
具有toMerge
没有的属性,那么它将保留这些属性。在那种情况下,它就不是克隆了。
@JasonRice 删除空的 并将其合并到 lodash.merge(mergeInto, toMerge)以上是关于Lodash - .extend() / .assign() 和 .merge() 之间的区别的主要内容,如果未能解决你的问题,请参考以下文章