shfshanyue / Daily-Question

互联网大厂内推及大厂面经整理,并且每天一道面试题推送。每天五分钟,半年大厂中
https://q.shanyue.tech
4.92k stars 508 forks source link

【Q665】JS 如何检测到对象中有循环引用 #683

Open shfshanyue opened 3 years ago

shfshanyue commented 3 years ago

示例,如下数据为循环结构/循环引用

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

追问:

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

TODO

yoyou commented 3 years ago
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))
iotale commented 3 years ago
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()

haotie1990 commented 3 years ago
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;
}
haotie1990 commented 3 years ago
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));
heretic-G commented 3 years ago

希望不可枚举和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
}
Hishengs commented 3 years ago
function isCircular(obj) {
    try {
      JSON.stringify(obj) 
    } catch(e) {
       return e.message.includes('Converting circular structure to JSON');
    }
    return false;
}
cloudGrin commented 9 months ago

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

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

cloudGrin commented 9 months ago
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;
}