首先介绍一下什么叫回环?
回环就是我们在定义一个对象的时候,存在内部对象的属性应用了对象内其它对象的内存地址,如果指向了同一个对象就会形成一个环,没有尽头。
例子如下:
var obj = { a: 1, b: 2, c: 3 };
obj.d = {
e: 4,
f: 5,
g: obj,
};
打印结果
上面这个例子,它就是一个回环对象,是没有出口的,如果我们通过JSON.stringify是无法转为字符串的,会抛出错误Uncaught TypeError: Converting circular structure to JSON。
然后我们来讲解一下回环的一个检测
回环的检测思路其实是通过递归的形式,将每一个对象属性的引用地址都存储起来,然后在每一次遇到对象的时候,判断是否已经存储了这个地址空间来判断是否存在回环的想象,在存储的时候必须要使用map这种存储方式,map支持多数据类型的存储。
var obj = { a: 1, b: 2, c: 3 };
obj.d = {
e: 4,
f: 5,
g: obj,
};
let map = new Map();
function deepClone(obj) {
if (typeof obj !== "object") return obj;
let newObj = new obj.__proto__.constructor();
map.set(obj, newObj);
for (let key in obj) {
if (typeof obj[key] === "object") {
if (!map.has(obj[key])) {
map.set(obj[key], true);
return deepClone(obj[key]);
} else {
return true;
}
}
}
return false;
}
console.log(deepClone(obj));
然后我们来讲解一下回环克隆实现方案
回环克隆其实跟检测的思路是差不多的,它主要是当我们每一次遇到对象属性的时候,需要将我们创建的对象存储起来,当下一次遇到同一个对象属性的时候,就直接将存储起来的数值进行一个赋予,不再进行深度拷贝,这样就避免了克隆时候存在的死循环现象。
var obj = { a: 1, b: 2, c: 3 };
obj.d = {
e: 4,
f: 5,
g: obj,
};
let map = new Map();
function deepClone(obj) {
// debugger;
if (typeof obj !== "object") return obj;
let newObj = new obj.__proto__.constructor();
map.set(obj, newObj);
for (let key in obj) {
if (typeof obj[key] === "object") {
if (!map.has(obj[key])) {
map.set(obj[key], newObj);
newObj[key] = deepClone(obj[key]);
} else {
newObj[key] = map.get(obj[key]);
}
} else {
newObj[key] = obj[key];
}
}
// 清空map数据,便于下次使用
map.clear();
return newObj;
}
var newObj = deepClone(obj);
以上纯属个人理解,代码也不是最优写法,各位小伙伴们有什么理解错误以及更好的实现方案,欢迎评论区留言,互相学习。