前言
循环引用存在的几种形式:
1. 对象的属性引用对象本身
直接引用最外层对象
const obj = {
a: 1
};
obj.b = obj;
引用对象的部分属性
let obj = {
name: 'hi',
child: {}
}
obj.child.obj = obj.child
2. 对象之间相互引用
const obj1 = {
a: 1
}
const obj2 = {
a: obj1,
b: 1
}
obj1.b = obj2
判断是否存在循环引用
源码实现
function hasCircularReference(obj) {
// 创建一个 Set,用于存储已经访问过的对象
const stackSet = new Set();
const detect = (obj) => {
if (stackSet.has(obj)) {
return true;
}
stackSet.add(obj);
for (const key in obj) {
if (typeof obj[key] === 'object' && obj[key] !== null) {
if (detect(obj[key])) {
return true;
}
}
}
// 平级检测完成之后,将当前对象删除,防止误判
// 例如:对象的属性指向同一引用,如果不删除的话,会被认为是循环引用
// let tempObj = { name: '00' }
// let obj4 = { obj1: tempObj, obj2: tempObj }
stackSet.delete(obj);
return false;
}
return detect(obj)
}
测试
// 1. 对象之间相互引用
let obj1 = { name: '00' }
let obj2 = { name: '11' }
// 对象1的属性引用了对象2
obj1.obj = obj2
// 对象2的属性引用了对象1
obj2.obj = obj1
console.log(hasCircularReference(obj1)) // true
console.log(hasCircularReference(obj2)) // true
// 2. 对象的属性引用了对象本身
let obj = { name: '00' }
// 对象的属性引用了对象本身
obj.child = obj
console.log(hasCircularReference(obj)) // true
// 3. 对象的属性引用部分属性
let obj3 = {
name: '00',
child: {}
}
obj3.child.obj = obj3.child
console.log(hasCircularReference(obj3)) // true
// 4. 对象的属性指向同一引用
let tempObj = {
name: '00'
}
let obj4 = {
obj1: tempObj,
obj2: tempObj
}
console.log(hasCircularReference(obj4)) // false
// 5. 其他数据类型
console.log(hasCircularReference(1)) // false
console.log(hasCircularReference('00')) // false
console.log(hasCircularReference(false)) // false
console.log(hasCircularReference(null)) // false
console.log(hasCircularReference(undefined)) // false
console.log(hasCircularReference([])) // false
console.log(hasCircularReference(Symbol('00'))) // false