今天见到一种很巧的deepClone的实现,之前写深拷贝代码都会写很长,而且各种判断;或者用JSON.parse(JSON.stringify(obj))的方式,但是这种方式如果有循环引用则会报错。

这段代码通过使用Message Channel巧妙地解决了循环引用的问题,实现了深拷贝。

1
2
3
4
5
6
7
8
9
function deepClone(obj) {
return new Promise((resolve) => {
const { port1, port2 } = new MessageChannel()
port1.postMessage(obj)
port2.onmessage = (msg) => {
resolve(msg.data)
}
})
}

不过需要注意的是,该代码仍然无法克隆function,也会丢失Symbol作为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
38
function deepCopy(obj, cache = []) {
if (obj === null || typeof obj !== 'object') {
return obj
}
const objType = Object.prototype.toString.call(obj).slice(8, -1)
// 考虑 Symbol对象的copy
if (objType === 'Symbol') {
return Symbol(obj.description)
}
// 考虑 正则对象的copy
if (objType === 'RegExp') {
return new RegExp(obj)
}
// 考虑 Date 实例 copy
if (objType === 'Date') {
return new Date(obj)
}
// 考虑 Error 实例 copy
if (objType === 'Error') {
return new Error(obj)
}
const hit = cache.filter((c) => c.original === obj)[0]
if (hit) {
return hit.copy
}
const copy = Array.isArray(obj) ? [] : {}
cache.push({ original: obj, copy })
Object.keys(obj).forEach((key) => {
copy[key] = this.deepCopy(obj[key], cache)
})
// 对key为Symbol类型的情况进行处理,拿到所有为Symbol类型的key
const symbolKeys = Object.getOwnPropertySymbols(obj)
// for...of遍历取出所有的key,存放到新对象中
for (const sKey of symbolKeys) {
copy[sKey] = deepCopy(obj[sKey], cache)
}
return copy
}