一、采用JSON字符串实现深拷贝
let obj = {
name:'zhangsan',
age:13,
hobby:['basketball','pingpang','dance'],
score:{
a:1,
b:2
}
}
// 使用JSON进行深拷贝
let res = JSON.parse( JSON.stringify(obj))
// 测试代码
res.score.a = 12
console.log(res,obj)
缺点:
1. 对象方法不会拷贝
2. 数组中的undefined会变为null,对象上的undefined不会拷贝
3. symbol不会拷贝
4. 正则表达式变成了空对象
5. Infinity会变为null
6. 一些类型的对象会被转换为字符串,比如:Date对象
7. 循环引用会报错
obj.score = obj
// 使用JSON进行深拷贝
let res = JSON.parse( JSON.stringify(obj))
8. 不能转换BigInt类型,会报错
二、递归实现
难点:
- 解决JSON字符串深拷贝的缺点,解决方式详见代码
- 循环引用问题
- 使用:数组或set保存已经处理过的数据,解决循环引用的问题(本案例使用数组)
// 封装一个获取数据类型的函数
function toType(data) {
return Object.prototype.toString.call(data).slice(8, -1)
}
// 使用递归方式处理深拷贝 ,参数1:要拷贝的数据,参数2:解决循环引用(初始不用传参)
const deepClone = function (target, done = []) {
// 处理一些特殊数据类型
// 如果是null或undefined,如果是就返回该值
if (target == null) return target
// 获取数据的类型,以及其构造器(避免后面频繁用到)
let type = toType(target)
let col = target.constructor // 当前
// 如果是正则或date返回新的实例
if (type === "RegExp" || type === "date") return new col(target)
// 如果是ERROR实例,返回该信息的实例
if (type === "Error") return new col(target.message)
// 如果是函数,返回一个新的函数包裹该函数
if (type === 'Function') return function () {
target.apply(this, arguments)
}
// 不是对象和数组(除以上的其他基本数据类型),则返回该数据
if (type !== "Array" && type !== "Object") return target
// 解决循环引用问题
// 如果已经处理过该数据,就直接返回该数据(不处理)
if (done.includes(target)) return target
// 没有处理过的数据就放入该数组中,在递归时将done做为第二个参数传入
done.push(target)
// 如果是数组或者对象,遍历递归
// 创建一个新的数组或对象,用于存储拷贝后的数据
let res = new col()
// 遍历对象或者数组
Object.keys(target).forEach(key => {
//递归,第二个参数处理循环引用问题
res[key] = deepClone(target[key], done)
})
return res
}