判断循环引用有两种方法

  1. JSON.stringify 方法将对象转换为字符串时,如果对象中存在循环引用,则会报错 TypeError: Converting circular structure to JSON。捕获这个错误,就可以很方便判断出是否有循环引用。
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
  1. 新建一个 Set 数据,用来记录已经遍历的对象,当遍历到的对象在 Set 中存在时,说明存在循环引用。当遇到子对象时,需要递归遍历,并将已经遍历过的对象也存在 Set 中。

不一定非得用 Set 数据结构,WeakSet、Map 或 WeekMap 都可以。

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

流程图