高级前端
js
【Q665】JS 如何检测到对象中有循环引用

JS 如何检测到对象中有循环引用

更多描述 示例,如下数据为循环结构/循环引用

const user = { id: 10086, name: "山月" };
user._user = user;

追问:

  1. 由于 JSON.stringify 序列化对象时,将跳过不枚举的 key,因此此时可不考虑不可枚举的 key
  2. 如果考虑不可枚举 key 与 Symbol 如何处理

Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 683 (opens in a new tab)

Author 回答者: shfshanyue (opens in a new tab)

TODO

Author 回答者: yoyou (opens in a new tab)

const a = {
a:1,
c: 3
}

const b = {
  a: a,
  c: 3
}
a.b = b;

//JSON.stringify(a);

const keyMap = new Map();
keyMap.set(a, "1");
keyMap.set(b, "2");
function circle(target) {
  const keys = Object.keys(target);
  for(let i = 0; i < keys.length; i++) {
    const key = keys[i];
    const val = target[key];
    if(keyMap.has(val)) {
      return true
    }else {
      keyMap.set(val, key)
      if(typeof val === 'object') {
        circle(val)
      }
    }
  }
  return false;
}
console.log(circle(a))

Author 回答者: iotale (opens in a new tab)

const a = {
a:1,
c: 3
}

const b = {
  a: a,
  c: 3
}
a.b = b;

//JSON.stringify(a);

const keyMap = new Map();
keyMap.set(a, "1");
keyMap.set(b, "2");
function circle(target) {
  const keys = Object.keys(target);
  for(let i = 0; i < keys.length; i++) {
    const key = keys[i];
    const val = target[key];
    if(keyMap.has(val)) {
      return true
    }else {
      keyMap.set(val, key)
      if(typeof val === 'object') {
        circle(val)
      }
    }
  }
  return false;
}
console.log(circle(a))

可以将 Object.keys() 替换为 Reflect.ownKeys()

Author 回答者: haotie1990 (opens in a new tab)

function isCircularReference(value) {
  const isObject = (value) =>
    Object.prototype.toString.call(value) === "[object Object]";
  const memory = new WeakMap();
  let isCycled = false;
  const traverse = function (value) {
    if (isObject(value)) {
      if (memory.has(value)) {
        isCycled = true;
        return;
      }
      memory.set(value, true);
      const keys = Object.keys(value);
      for (const key of keys) {
        traverse(value[key]);
      }
    }
  };
  traverse(value);
  return isCycled;
}

Author 回答者: haotie1990 (opens in a new tab)

function isCircularReference(value) {
  const isObject = (value) =>
    Object.prototype.toString.call(value) === "[object Object]";
  const memory = new WeakMap();
  let isCycled = false;
  const traverse = function (value) {
    if (isObject(value)) {
      if (memory.has(value)) {
        isCycled = true;
        return;
      }
      memory.set(value, true);
      const keys = Object.keys(value);
      for (const key of keys) {
        traverse(value[key]);
      }
    }
  };
  traverse(value);
  return isCycled;
}
// isObject改为isPrimitive
const isPrimitive = (value) =>
  /Number|Boolean|String|Undefined|Null|Symbol/.test(
    Object.prototype.toString.call(value),
  );

Author 回答者: heretic-G (opens in a new tab)

希望不可枚举和Symbol 可以Reflect.ownKeys(obj).map(curr => obj[curr]) 获取全部的key对应的values Object上面也有 不过是分开的 两个API

function checkObj(obj, set = new Set()) {
  if ((typeof obj === "object" && obj !== null) || Array.isArray(obj)) {
    if (set.has(obj)) {
      return true;
    } else {
      set.add(obj);
    }
    return Object.values(obj).some((curr) => {
      return checkObj(curr, set);
    });
  }
  return false;
}

Author 回答者: Hishengs (opens in a new tab)

function isCircular(obj) {
  try {
    JSON.stringify(obj);
  } catch (e) {
    return e.message.includes("Converting circular structure to JSON");
  }
  return false;
}

Author 回答者: cloudGrin (opens in a new tab)

上面没有一个答案是对的啊,试试这个对象返回的都是true

const c = {}; const d = { e:c, f:c }

Author 回答者: cloudGrin (opens in a new tab)

function hasCircularReference(obj, seenObjects = new Set()) {
  if (typeof obj !== "object" || obj === null) {
    return false;
  }
 
  if (seenObjects.has(obj)) {
    return true;
  }
 
  seenObjects.add(obj);
 
  for (let key in obj) {
    if (hasCircularReference(obj[key], new Set([...seenObjects]))) {
      return true;
    }
  }
 
  return false;
}