deepmerge源码 🔗
最近在看webpack-chain
源码的时候,看到内部在合并webpack配置的时候使用的是deepmerge
这个第三方库,看了一下源码一共一百来行,简单学习一下
https://github.com/TehShrike/deepmerge
使用 🔗
主要功能就是实现两个对象的合并,通key的情况下会进行合并
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| const x = {
foo: { bar: 3 },
array: [{
does: 'work',
too: [ 1, 2, 3 ]
}]
}
const y = {
foo: { baz: 4 },
quux: 5,
array: [{
does: 'work',
too: [ 4, 5, 6 ]
}, {
really: 'yes'
}]
}
const output = {
foo: {
bar: 3,
baz: 4
},
array: [{
does: 'work',
too: [ 1, 2, 3 ]
}, {
does: 'work',
too: [ 4, 5, 6 ]
}, {
really: 'yes'
}],
quux: 5
}
merge(x, y)
|
源码 🔗
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| function deepmerge(target, source, options) {
options = options || {}
options.arrayMerge = options.arrayMerge || defaultArrayMerge
options.isMergeableObject = options.isMergeableObject || defaultIsMergeableObject
options.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified
var sourceIsArray = Array.isArray(source)
var targetIsArray = Array.isArray(target)
var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray
if (!sourceAndTargetTypesMatch) {
return cloneUnlessOtherwiseSpecified(source, options)
} else if (sourceIsArray) {
return options.arrayMerge(target, source, options)
} else {
return mergeObject(target, source, options)
}
}
|
cloneUnlessOtherwiseSpecified 🔗
是否克隆出一个新对象,默认行为是进行克隆, 最终还是都的deepmerge主函数
1
2
3
4
5
| function cloneUnlessOtherwiseSpecified(value, options) {
return (options.clone !== false && options.isMergeableObject(value))
? deepmerge(emptyTarget(value), value, options)
: value
}
|
defaultArrayMerge 🔗
默认同为数组时进行合并的逻辑
1
2
3
4
5
| function defaultArrayMerge(target, source, options) {
return target.concat(source).map(function(element) {
return cloneUnlessOtherwiseSpecified(element, options)
})
}
|
利用数组concat返回一个新元素的情况,并且遍历数组元素进行创建新元素
mergeObject 🔗
合并对象的主逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| function mergeObject(target, source, options) {
var destination = {}
if (options.isMergeableObject(target)) {
getKeys(target).forEach(function(key) {
destination[key] = cloneUnlessOtherwiseSpecified(target[key], options)
})
}
getKeys(source).forEach(function(key) {
if (propertyIsUnsafe(target, key)) {
return
}
if (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) {
destination[key] = getMergeFunction(key, options)(target[key], source[key], options)
} else {
destination[key] = cloneUnlessOtherwiseSpecified(source[key], options)
}
})
return destination
}
|
其中getKeys
实现比较有意思
1
| Object.keys(target).concat(getEnumerableOwnPropertySymbols(target))
|
首先使用Object.keys
获取到对象可枚举属性组成的数组
再使用Object.getOwnPropertySymbols
拿到对象上所有Symbol属性的数组
这里就比较严谨了,兼融了对象上的可以访问的属性
- 创建一个新的空对象
- 遍历目标对象,把目标对象上的属性进行复制
- 遍历源对象
- 如果目标对象上有对应的同属性,进行复制合并
- 没有的话进行创建新的值
笔记 🔗
in
和hasOwnProperty
区别- 都可以用来对象内是否含有对应属性
- 都支持判断ES6内的
symbols
- 不同点就是
in
可以判断出原型链上通过继承过来的属性, hasOwnProperty
只能判断对象上自己拥有的属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| const obj = { answer: 42 };
'answer' in obj;
obj.hasOwnProperty('answer');
'does not exist' in obj;
obj.hasOwnProperty('does not exist');
const symbol = Symbol('answer');
const obj = { [symbol]: 42 };
symbol in obj;
obj.hasOwnProperty(symbol);
'constructor' in obj;
'__proto__' in obj;
'hasOwnProperty' in obj;
obj.hasOwnProperty('constructor');
obj.hasOwnProperty('__proto__');
obj.hasOwnProperty('hasOwnProperty');
class BaseClass {
get baseProp() {
return 42;
}
}
class ChildClass extends BaseClass {
get childProp() {
return 42;
}
}
const base = new BaseClass();
const child = new ChildClass();
'baseProp' in base;
'childProp' in child;
'baseProp' in child;
base.hasOwnProperty('baseProp');
child.hasOwnProperty('childProp');
child.hasOwnProperty('baseProp');
|
