关于javascript:Lodash – .extend()/ .assign()和.merge()之间的区别

Lodash - difference between .extend() / .assign() and .merge()

在Lodash库中,是否可以有人更好地解释合并和扩展/分配。

这是一个简单的问题,但我还是没有回答。


下面是extendassign的工作原理:对于源代码中的每个属性,将其值复制到目的地。如果属性值本身是对象,则不会对其属性进行递归遍历。整个对象将从源获取并设置为目标。

下面是merge的工作原理:对于源代码中的每个属性,检查该属性是否是对象本身。如果是,则递归向下,尝试将子对象属性从源映射到目标。所以本质上我们合并了从源到目标的对象层次结构。而对于extend/assign来说,它只是从源到目的地的属性的一级拷贝。

下面是一个简单的jsbin,它将使这个水晶清晰:http://jsbin.com/uxaqima/2/edit?控制台

下面是在示例中包含数组的更详细的版本:http://jsbin.com/uxaqima/1/edit?控制台


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的别名,所以它们是相同的。
  • 它们似乎都修改了目标对象(第一个参数)
  • 他们都是一模一样的

差异

  • _.defaults_.defaultsDeep的处理顺序与其他的相反(尽管第一个参数仍然是目标对象)。
  • _.merge_.defaultsDeep将合并子对象,其他对象将在根级别覆盖。
  • 只有_.assign_.extend会用undefined覆盖一个值。

测验它们都以类似的方式处理根节点上的成员。

1
2
3
4
_.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但其他人会跳过它

1
2
3
4
_.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的方式都一样

1
2
3
4
_.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将合并子对象

1
2
3
4
_.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" }}

似乎他们都不会合并数组

1
2
3
4
_.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"  ] }

全部修改目标对象

1
2
3
4
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将数组视为对象,其中键是数组的索引。

1
2
3
4
5
6
7
8
9
_.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"  ]


另一个需要注意的区别是处理undefined值:

1
2
3
4
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值合并为定义值。


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

分配

1
2
   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)

②合并

1
2
   merge is like assign but does not assign objects but replicates them instead.
  (deep copy)

默认值

1
2
   provides default values for missing values.
   so will assign only values for keys that do not exist yet in the source.

默认深度

1
2
   works like _defaults but like merge will not simply copy objects
   and will use recursion instead.

我相信,从语义的角度学习这些方法会让你更好地"猜测"现有和不存在价值的所有不同情况下的行为。


如果您希望在保留相同的obj引用的同时不进行覆盖的深度复制

obj = _.assign(obj, _.merge(obj, ))