一个完善的深拷贝需要能拷贝很多东西:

  • 基本类型数据
  • string为键的属性
  • symbol为键的属性
  • 日期和正则数据
  • map和set数据
  • 函数(但是一般不会需要对其深拷贝)
  • 对象的原型
  • 不可枚举属性
  • 循环引用
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
const deepClone = (value) => {
const map = new WeakMap()

// 判断是否为对象
const isObj = (value) => {
return (typeof value === 'object' && value) || typeof target === 'function'
}

// 拷贝核心
const core = (value) => {

// 原始类型
if (!isObj(value)) {
return value
}

// 函数
if (typeof value === 'function') {
return new Function('return ' + value.toString())()
}

// 日期和正则
if ([Date, RegExp].includes(value.constructor)) {
return new value.constructor(value)
}

// 判断是否已被拷贝,防止循环引用
const copied = map.get(value)
if (!!copied) return copied

// Map
if (value instanceof Map) {
const copy = new Map()
// 标记已拷贝
map.set(value, copy)
value.forEach((v, k) => {
if (isObj(v)) {
copy.set(k, core(v))
} else {
copy.set(k, v)
}
})
return copy
}

// Set
if (value instanceof Set) {
const copy = new Set()
map.set(value, copy)
value.forEach(v => {
if (isObj(v)) {
copy.add(core(v))
} else {
copy.add(v)
}
})
return copy
}

// 数组和对象
// Symbol也要被复制
const keys = Reflect.ownKeys(value)
// 属性描述符也要被复制
const allDesc = Object.getOwnPropertyDescriptors(value)

let copy

// 数组
if (Array.isArray(value)) {
copy = []
}
// 对象
else {
// 原型也要被复制
copy = Object.create(Object.getPrototypeOf(value), allDesc)
}

map.set(value, copy)
keys.forEach(key => {
const v = value[key]
if (isObj(v)) {
copy[key] = core(v)
} else {
copy[key] = v
}
})
return copy
}

return core(value)
}

不过对于大多数场景来说能克隆基本类型数据和普通对象就足够了,这种简单场景可以用JSON互转实现