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

IT技术 javascript lodash
2021-02-05 22:18:19

Lodash库中,有人可以对合并扩展/分配提供更好的解释

这是一个简单的问题,但答案仍然回避了我。

5个回答

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

下面是如何merge工作的:对于源的每个属性,检查如果该属性是对象本身。如果是,则递归向下并尝试将子对象属性从源映射到目标。所以本质上我们合并了从源到目标的对象层次结构。而对于extend/ assign,它是从源到目标的简单属性的一级副本。

这是一个简单的 JSBin,可以让这一切变得清晰:http ://jsbin.com/uXaqIMa/2/edit?js, console

这是示例中包含数组的更详细的版本:http : //jsbin.com/uXaqIMa/1/edit?js,console

@JasonRice 他们不会被打败。例如,在这个小提琴中,“a”属性不会被破坏确实,在扩展之后,dest["p"]["y"] 不再存在——这是因为在扩展之前 src 和 dest 都有一个“p”属性,所以 dest 的“p”属性被完全覆盖通过 src 的“p”属性(它们现在是完全相同的对象)。
2021-03-14 22:18:19
无论返回什么,它们似乎都会改变目标对象。
2021-03-21 22:18:19
如果源对象中不存在 _.extend clobbers 目标对象的成员,这似乎也让我感到惊讶。
2021-03-26 22:18:19
需要明确的是,这两种方法都通过引用修改/覆盖第一个参数。所以如果你想从结果合并中得到一个新对象,最好传递一个对象文字。var combined = merge({}, src, dest)
2021-04-04 22:18:19
一个重要的区别似乎是 _.merge 返回一个新的合并对象,而 _.extend 就地改变了目标对象,
2021-04-11 22:18:19

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"  ]
它实际上像合并对象一样合并数组,因为数组是带有数字键的对象。我同意人们会期望连接或替换数组,这取决于使用情况。
2021-03-15 22:18:19
优秀的答案。测试非常有教益:-)
2021-03-19 22:18:19
从 v4.0 开始,_.extend 现在是 _.assignIn 的别名,而不是 _assign。assignIn 函数添加了对继承属性的处理。
2021-03-24 22:18:19
此处是否将 null 视为与 undifined 相同?
2021-04-06 22:18:19
_.extend is an alias for _.assign, so they are identicalOnly _.assign will overwrite a value with undefined
2021-04-10 22:18:19

另一个需要注意的区别是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”对象的克隆?
2021-03-19 22:18:19
如果mergeInto有没有的属性,toMerge那么它会保留这些属性。在那种情况下,它不会是克隆。
2021-03-25 22:18:19
@JasonRice 删除空的 {} 并将其合并到位 lodash.merge(mergeInto, toMerge)
2021-04-03 22:18:19

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

_。分配

   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)

_.默认值

   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.

我相信学习从语义的角度思考这些方法会让你更好地“猜测”存在和不存在值的所有不同场景的行为。

如果您想要一个没有覆盖的深层复制同时保留相同的obj引用

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