我们在实际开发中,经常需要深拷贝一个对象,我一般喜欢使用JSON.parse(JSON.stringify())
,方便好用。不过如果对象的value值为 Symbol
、Function
和undefined
时,这些值就会被忽略。
比如
const obj = {
a: Symbol(2),
b: undefined,
c: () => {},
d: 3
}
JSON.parse(JSON.stringify(obj)) // {d: 3}
还有如果value值不是一个普通的对象,而是一个Map或者Set对象,用上面的方法就会拷贝成一个普通的对象。所以在面对一些特殊情况下时,就不太好用了。下面写下我在特殊情况下使用的方法
function deepClone(obj, hash = new WeakMap()) {
// 如果对象的值等于它本身,在递归的时候会陷入死循环,通过WeakMap模拟hash表,避免这种情况的出现
if (hash.has(obj)) return hash.get(obj)
let type = Object.prototype.toString.call(obj)
let target;
switch (type) {
// 普通对象需要考虑下key值为Symbol的情况
case "[object Object]":
let symKeys = Object.getOwnPropertySymbols(obj)
target = {}
if (symKeys.length) {
symKeys.forEach(key => {
target[key] = deepClone(obj[key], hash)
})
}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object') {
target[key] = deepClone(obj[key], hash)
} else {
target[key] = obj[key]
}
}
}
break;
case "[object Array]":
target = []
obj.forEach(val => {
if (typeof val === 'object') {
target.push(deepClone(val, hash))
} else {
target.push(val)
}
})
break;
case "[object Map]":
// 调用map对象的forEach方法遍历所有的键值对
target = new Map()
obj.forEach((val, key) => {
target.set(key, deepClone(val, hash))
})
break;
case "[object Set]":
target = new Set()
obj.forEach(val => {
target.add(deepClone(val, hash))
})
break;
case "[object Date]":
target = new Date(obj)
break;
default:
target = obj
break;
}
return target
}