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

JDi*_*522 448 javascript lodash

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

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

Shi*_*hah 563

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

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

这里有简单的JSBin,可以清晰地表达这一点:http://jsbin.com/uXaqIMa/2/edit?js, console

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

  • 它们似乎都会改变目标对象,无论它们返回什么. (64认同)
  • 一个重要的区别似乎是当_.merge返回一个新的合并对象时,_. extend会就地改变目标对象, (15认同)
  • 要清楚,两种方法都通过引用修改/覆盖*first*参数.因此,如果您想从生成的合并中获取新对象,最好传递对象文字.`var combined = merge({},src,dest)` (11认同)
  • 如果它们不存在于源对象中,那么_.extend目标对象的成员也会出现,这对我来说太令人惊讶了. (7认同)
  • @JasonRice他们没有遭到破坏.[例如,在这个小提琴中,"a"属性不会被破坏](http://jsbin.com/tocavuqami/1/edit?js,console).确实,在扩展之后,dest ["p"] ["y"]不再存在 - 这是因为在扩展src和dest之前都有一个"p"属性,所以dest的"p"属性被完全覆盖通过src的"p"属性(它们现在是完全相同的对象). (5认同)

Nat*_*ate 504

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"  }
Run Code Online (Sandbox Code Playgroud)

_.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" }
Run Code Online (Sandbox Code Playgroud)

他们都处理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 }
Run Code Online (Sandbox Code Playgroud)

但是,只有_.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" }}
Run Code Online (Sandbox Code Playgroud)

它们似乎都没有合并数组

_.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"  ] }
Run Code Online (Sandbox Code Playgroud)

全部修改目标对象

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" }
Run Code Online (Sandbox Code Playgroud)

没有真正按预期在阵列上工作

注意:正如@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"  ]
Run Code Online (Sandbox Code Playgroud)

  • 它实际上合并了数组,就像合并对象一样,因为数组是带有数字键的对象.我同意人们期望连接或替换数组取决于使用情况. (26认同)
  • 很好的答案.测试非常有说服力:-) (10认同)
  • 从v4.0开始,_. extend现在是_.assignIn的别名,而不是_assign.assignIn函数添加处理继承的属性. (9认同)
  • `_.extend是_.assign的别名,所以它们是相同的`与'只有_的冲突.将使用undefined覆盖一个值 (5认同)
  • null处理与此处的unifined相同? (2认同)
  • lodash 4 有一个 `mergeWith(obj,sources,customizer)` 方法。定制器给出的具体示例是一个实现数组串联的示例。 (2认同)

sam*_*amz 74

另一个需要注意的区别是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}
Run Code Online (Sandbox Code Playgroud)

因此merge不会将undefined值合并到定义的值中.

  • 如果`mergeInto`具有`toMerge`没有的属性,那么它将保留这些属性.在那种情况下,它不会是一个克隆. (6认同)
  • 它只是我或者是否使lodash.extend完全无用,因为它总是返回'toMerge'对象的克隆? (3认同)

epe*_*leg 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)
Run Code Online (Sandbox Code Playgroud)

_.合并

   merge is like assign but does not assign objects but replicates them instead.
  (deep copy)
Run Code Online (Sandbox Code Playgroud)

_.defaults

   provides default values for missing values.
   so will assign only values for keys that do not exist yet in the source.
Run Code Online (Sandbox Code Playgroud)

_.defaultsDeep

   works like _defaults but like merge will not simply copy objects
   and will use recursion instead.
Run Code Online (Sandbox Code Playgroud)

我相信从语义的角度学习思考这些方法可以让你更好地"猜测"现有和非现有值的所有不同场景的行为.


mba*_*o01 5

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

obj = _.assign(obj, _.merge(obj, [source]))
Run Code Online (Sandbox Code Playgroud)