# 手动实现一个深拷贝
简单递归,深层遍历对象
function deepClone (obj) {
let newObj = {}
for(let item in obj) {
if(typeof obj[item] !== 'object') {
newObj[item] = obj[item]
} else {
newObj[item] = deepClone(obj[item])
}
}
return newObj
}
var obj1 = {
name: 'linjiaheng',
age: 23,
partner: ['laihengcong', 'quanzhiyuan', 'penghaohao']
}
var obj2 = deepClone(obj1)
obj2.name = 'xielin'
obj2.age = 24
obj2.partner[0] = 'xiaoxiao'
console.log(obj2)
console.log(obj1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 存在的问题
- 传入的对象参数没有校验,可能是
null/Date/RegExp/Array
// 判断传入的参数
function deepClone(obj) {
if(!isObject(obj)) return obj;
if(obj == null) return null;
if(obj instanceof Date) return new Date(obj)
if(obj instanceof RegExp) return new RegExp(obj);
if(Object.prototype.toString.call(obj) === '[object Array]') {
// 处理数组
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 判断对象
function isObject(x) {
return Object.prototype.toString.call(x) === '[object Object]'
}
1
2
3
2
3
- 若为数组,或者为
Set Map weakSet weakMap
// 考虑数组兼容
var target = Array.isArray(obj) ? [] : {}
1
2
2
- 解决循环引用问题
所谓的循环检测非常简单,我们设置一个数组或者哈希表用来存储已经拷贝过的对象,检测当前对象是否已经存在于哈希表中,如果有,取出该值,然后返回。
function deepClone(obj, map=new Map()) {
if(!isObject(obj)) return obj;
// 循环检测,如果存在直接返回该值
if(map.has(obj)) return map.get(obj)
// 判断数组
var target = Array.isArray(obj) ? [] : {}
map.set(obj, target)
// 遍历拷贝
for(let item in obj) {
if(obj.hasOwnProperty(key)) {
if(isObject(obj[key])) {
target[key] = deepClone(obj[key], map)
} else {
target[key] = obj[key]
}
}
}
return target
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 递归容易爆栈(层次很深的时候) 之前的文章破解递归爆栈
# 按照数据类型实现一个深拷贝
// 判断类型
function getType (target) {
return Object.prototype.toString.call(target).slice(8, -1)
}
// 判断是否是原始类型类型.
// 对应可引用的数据类型,需要递归遍历;对应不可引用的数据类型,直接复制即可
function isReferenceType (target) {
let type = typeof target
return (target !== null && (type === 'object' || type === 'function'))
}
// 获取原型上的方法
function getInit (target) {
let ClassNames = target.constructor
return new ClassNames()
}
// 引用类型
const mapTag = 'Map'
const setTag = 'Set'
const arrayTag = 'Array'
const objectTag = 'Object'
// 不可引用类型
const boolTag = 'Boolean'
const dateTag = 'Date'
const errorTag = 'Error'
const numberTag = 'Number'
const regexpTag = 'RegExp'
const stringTag = 'String'
const symbolTag = 'Symbol'
const bufferTag = 'Uint8Array'
let deepTag = [mapTag, setTag, arrayTag, objectTag]
function deepClone(target,map=new Map()) {
let type = getType(target)
let isOriginType = isReferenceType(target)
if(!isOriginType) {return target} // 对于不可引用的数据类型,直接复制即可
let cloneTarget
if(deepTag.includes(type)){
cloneTarget = getInit(target)
}
// 防止循环引用
if(map.get(target)) {
return map.get(target)
}
map.set(target,cloneTarget)
// 如果是mapTag类型
if(type === mapTag) {
target.forEach((v,key) => {
cloneTarget.set(key,deepClone(v,map))
})
return cloneTarget
}
if(type === setTag) {
target.forEach((v) => {
cloneTarget.add(deepClone(v,map))
})
return cloneTarget
}
if (type === arrayTag) {
target.forEach((v, i) => {
cloneTarget[i] = deepClone(v, map)
})
return cloneTarget
}
// 如果是 objectTag 类型
if (type === objectTag) {
let array = Object.keys(target)
array.forEach((i, v) => {
cloneTarget[i] = deepClone(target[i], map)
})
return cloneTarget
}
}
const map = new Map()
map.set('key', 'value')
map.set('name', 'kaka')
const set = new Set()
set.add('11').add('12')
console.log(JSON.stringify(map))
console.log(JSON.stringify(new Set([1,2])))
const target = {
field1: 1,
field2: undefined,
field3: {
child: 'child'
},
field4: [
2, 8
],
empty: null,
map,
set,
test: {
name: 'linjiaheng',
skill: ['js','css']
}
}
target.target = target
const target1 = deepClone(target)
target.test.name = '谢琳'
console.log('🍎', target)
console.log('🍌', target1)
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
← 牛客网评测 手写一个Promise →