在对于对象的深拷贝过程中主要碰到了三个问题:
1、需要兼容各种数据类型,除了object外,还有数组、symbol还有set等数据结构(本文只兼容了数组、symbol类型)
2、需要解决拷贝对象内的循环引用问题(使用数组记录拷贝过的对象,记录一个拷贝对象的数据结构,{source:原拷贝对象,target:拷贝后的对象})
3、使用递归的方式可能会造成爆栈,解决办法就是采用迭代的方式
递归的方式
//存放已拷贝的对象用于循环引用检测
let objArr = [];
function deepCopy(obj) {
//判断循环引用检测
for (let ele of objArr) {
if (obj === ele.source) {
return ele.target;
}
}
//拷贝容器
let newObj = {};
//将拷贝的对象放入数组中用于循环引用检测
objArr.push({
source: obj, //被拷贝对象上的原引用对象,用于循环检测比对
target: newObj
})
//使用Reflect可以检测到Symbol类型的属性
Reflect.ownKeys(obj).forEach(key => {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object') {
//使用Array.from对拷贝的数组进行处理
newObj[key] = Object.prototype.toString.call(obj[key]) === '[object Array]' ? Array.from(deepCopy(obj[key], key)) : deepCopy(obj[key], key);
} else {
//属性值为原始类型的值
newObj[key] = obj[key];
}
}
})
return newObj;
}
迭代的方式
将待拷贝的对象放入栈中,循环直至栈为空,解决了递归方法的爆栈问题;
function cloneForce(x) {
//拷贝对象记录
const uniqueList = [];
let root = {};
// 循环数组
const loopList = [{
parent: root,
key: undefined,
data: x,
}];
while (loopList.length) {
//深拷贝,元素出栈
const node = loopList.pop();
const parent = node.parent;
const key = node.key;
const data = node.data;
let res = parent;
if (typeof key !== 'undefined') {
res = parent[key] = {};
}
// 判断数据是否存在
let uniqueData = find(uniqueList, data);、
//数据存在
if (uniqueData) {
parent[key] = uniqueData.target;
break; // 中断本次循环
}
//数据不存在,将其放入数组
uniqueList.push({
source: data,
target: res,
});
for (let k in data) {
if (data.hasOwnProperty(k)) {
if (typeof data[k] === 'object') {
// 下一次循环
loopList.push({
parent: res,
key: k,
data: data[k],
});
} else {
res[k] = data[k];
}
}
}
}
return root;
}
function find(arr, item) {
for (let i = 0; i < arr.length; i++) {
if (arr[i].source === item) {
return arr[i];
}
}
return null;
}