一、采用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)
缺点:
- 对象方法不会拷贝
- 数组中的undefined会变为null,对象上的undefined不会拷贝
- symbol不会拷贝
- 正则变成了空对象
- Infinite会变为null
一些类型的对象会被转换为字符串
let obj = {
c:new Date()
}
// 使用JSON进行深拷贝
let res = JSON.parse( JSON.stringify(obj))
console.log(res,obj)
// res: c: '2022-02-23T03:15:43.266Z'
// obj: c: Wed Feb 23 2022 11:15:43 GMT+0800 (中国标准时间)
循环引用会报错
obj.score = obj
// 使用JSON进行深拷贝
let res = JSON.parse( JSON.stringify(obj))
不能转换BigInt类型
二、递归实现
封装一个函数,解决json字符串深拷贝产生的问题 处理循环调用问题,可以使用:数组或set保存已经处理过的数据,解决循环引用的问题(本案例使用数组)
// 封装一个获取数据类型的函数
function toType(data) {
return Object.prototype.toString.call(data).slice(8, -1)
}
// 使用递归方式处理深拷贝 ,参数1:要拷贝的数据,参数2:解决循环引用(不用传参)
const deepClone = function (target, done = []) {
// 处理一些特殊数据类型
if (target == null) return target // 如果是null或undefined,如果是就返回该值
// 获取数据的类型,以及其构造器(避免后面频繁用到)
let type = toType(target)
let col = target.constructor
if (type === "RegExp" || type === "date") return new col(target) // 如果是正则或date返回新的实例
if (type === "Error") return new col(target.message) // 如果是ERROR实例,返回该信息的实例
if (type === 'Function') return function () { // 如果是函数,返回一个新的函数包裹该函数
target.apply(this, arguments)
}
if (type !== "Array" && type !== "Object") return target // 不是对象和数组(除以上的其他基本数据类型),则返回该数据
// 解决循环引用问题
if (done.includes(target)) return target // 如果已经处理过该数据,就直接返回该数据(不处理)
done.push(target) // 没有处理过的数据就放入该数组中,在递归时将done做为第二个参数传入
// 数组或者对象,遍历递归
let res = new col() // 创建一个新的数组或对象,用于存储拷贝后的数据
Object.keys(target).forEach(key => { // 遍历对象或者数组
res[key] = deepClone(target[key], done) //递归,第二个参数处理循环引用问题
})
return res
}