underscore是一个JavaScript函数库,在不对JavaScript本身进行扩展的基础上提供了一些惯用操作,用来处理JavaScript原生并不提供的功能。其中在合并对象object的时候,我们经常会用到extend, merge,那么它们之间到底有什么区别呢?
使用方法对比
其实我们要对比的,是5个方法,在最新的underscore中,新增了assignIn,下文会说到。这里是3.10.1版本。
_.merge(object, [sources], [customizer], [thisArg]) _.assign(object, [sources], [customizer], [thisArg]) _.extend(object, [sources], [customizer], [thisArg]) _.defaults(object, [sources])_.defaultsDeep(object, [sources])
相同之处
- 都不能像你想要的一样处理数组array
_.extend
是_.assign
的别名,所以其实它们是一样的(在4.0版本之后提供了_.assignIn
方法,用以表达“继承”这个概念,_.extend变成了_.assignIn的别名)- 它们都会改变第一个参数object,也就是说执行完后,第一个参数object在内存中会改变
不同之处
_.defaults
和_.defaultsDeep
处理参数时,是反向处理的,也就是先读取后面的参数,然后往前推,但是最终被修改的,还是第一个参数_.merge
和_.defaultsDeep
会深处理,也就是顶级元素相同的情况下,会一层一层的比较子元素,而其他的只会比较顶级元素,如果顶级元素有一点不同,就直接处理掉- 只有
_.extend/_.assign
会把undefined作为处理后的值,其他的遇到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" }
看看_.defauts和_.defaultsDeep。
_.extend({},{a:'1'},{a:'2'}); // => {a:'2'} _.defaults({},{a:'1'},{a:'2'}); // => {a:'1'} 因为default是反着来的,从后面的参数往前推,_.defaultsDeep道理一样
_.assign/_.extend遇到undefined也会使用,而其他的会跳过。
_.assign({}, { a: 'a' }, { a: undefined }) // => { a: undefined },_.extend一样,因为是别名嘛 _.merge({}, { a: 'a' }, { a: undefined }) // => { a: "a" } _.defaults({}, { a: undefined }, { a: 'bb' }) // => { a: "bb" } _.defaultsDeep({}, { a: undefined }, { a: 'bb' }) // => { a: "bb" }
_.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" ] }
有一点需要解释,数组也是object,只不过是特殊的object,也就是键名为数字的按顺序排列的object,所以上面这条规则只是告诉我们不能对比数组的内部结构,但是可以用来深拷贝数组。你可以这样理解,数组的键名是隐形的,但是还是可以一一对应。
var newArray = _.extend([],['a','b'],['c']); // => ['c','b'] 键名为0的属性值被替换
因此,你不可以用其中任何一个方法作为合并数组的方法来使用,比如,你想合并{a:['aa']}和{a:['bb']}得到{a:['aa','bb']},这是不可能直接实现的。数组处理,请使用JavaScript原生的数组处理方法来处理。
所有的方法都是第一个参数被处理,该对象原始数据会被改变(同时被用作返回值返回)。
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" }
注:本文根据这个问题中的回答编写。
2016-08-30 4688 underscore