在递归遍历或处理对象时,循环引用会导致无限递归,最终引发栈溢出错误。检测循环引用可以避免这种问题。
判断循环引用有两种方法
- JSON.stringify 方法将对象转换为字符串时,如果对象中存在循环引用,则会报错 TypeError: Converting circular structure to JSON。捕获这个错误,就可以很方便判断出是否有循环引用。
javascript
function hasCircularReferences(obj) {
let result = false;
try {
JSON.stringify(obj);
} catch (error) {
if (error.message.indexOf("circular") > -1) {
result = true;
}
}
return result;
}
const a = {};
const b = {};
a.a = a;
hasCircularReferences(a); // true
hasCircularReferences(b); // false
- 新建一个 Set 数据,用来记录已经遍历的对象,当遍历到的对象在 Set 中存在时,说明存在循环引用。当遇到子对象时,需要递归遍历,并将已经遍历过的对象也存在 Set 中。
不一定非得用 Set 数据结构,WeakSet、Map 或 WeekMap 都可以。
javascript
function hasCircularReferences(obj, set = new Set()) {
// 异常类型判断
if (obj === null || typeof obj !== "object") {
return false;
}
// 递归遍历的结束条件
if (set.has(obj)) {
return true;
}
// 存储遍历过的对象
set.add(obj);
for (let key in obj) {
// 递归遍历子对象
if (hasCircularReferences(obj[key], set)) {
return true;
}
}
return false;
}
const a = {};
const b = {};
a.a = a;
hasCircularReferences(a); // true
hasCircularReferences(b); // false
流程图