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

时间:2013-11-13 22:33:45

标签: javascript lodash

Lodash库中,有人可以更好地解释mergeextend / assign

这是一个简单的问题,但答案却避开了我。

5 个答案:

答案 0 :(得分:541)

以下是extend / assign的工作原理:对于源中的每个属性,将其值按原样复制到目标。如果属性值本身是对象,则不会对其属性进行递归遍历。整个对象将从源获取并设置到目标。

以下是merge的工作原理:对于源中的每个属性,检查该属性是否为对象本身。如果它然后递归下去并尝试将子对象属性从源映射到目标。基本上我们将对象层次结构从源合并到目标。对于extend / assign,它是从源到目标的简单的一级属性副本。

这是一个简单的JSBin,可以使这个结晶清晰: http://jsbin.com/uXaqIMa/2/edit?js,console

这里有更复杂的版本,包括示例中的数组: http://jsbin.com/uXaqIMa/1/edit?js,console

答案 1 :(得分:472)

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

答案 2 :(得分:73)

要注意的另一个不同之处是处理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 :(得分:20)

从语义的角度考虑它们的作用可能也是有帮助的:

_.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 :(得分:0)

如果您想在不覆盖的情况下进行深拷贝,同时保留相同的obj参考

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