如何判断一个js对象是否存在循环引用

一、背景

在前端JSON.stringfy是我们常用的一个方法,可以将一个对象序列化。 例如将如下对象序列化

javascript 复制代码
const person = { name: 'kalory', age:18}

JSON.stringfy(person)
// 结果
'{"name":"kalory","age":18}'

将一个数组序列化

const arr = [1,2,3,4,5]
// 结果
'[1,2,3,4,5]'

const persons = [{ name: 'kalory', age:18},{ name: 'jack', age:48}]
// 结果
'[{"name":"kalory","age":18},{"name":"jack","age":48}]'

我们发现上面对象是可以使用JSON.stringfy序列化的。

  • 但是如果一个对象存在循环引用,序列化会报错,如下 person对象的owner属性指向了自己,存在循环引用
javascript 复制代码
const person = { name: 'kalory', age:18}
person.onwer = person

我们对上面这个对象进行JSON.stringfy,结果如下,会报错:

我们发现他说不能转化一个"圈结构体为JSON",是因为这个对象的owner属性指向了自己。在转化的时候会变成死循环。

  • 那么我们如果判断一个对象有没有环呢?

二、实现

2.1 思路

我们判断一个对象有没有循环引用,我们其实并不需要在乎对象的key是什么,只需要判断对象的value。如果value是引用数据类型的时候,有没有指向对象的某一值。

所以我们可以先使用Object.values()拿到对象所有values,然后定义一个cache变量用来存储values中的引用数据类型,然后遍历values

如果cache中存在,那么说明这个对象有环 return true,如果不存在并且是引用数据类型,那么我们就加入cache。当values遍历完都没有reutrn那么说明没有环return false

2.2 递归实现

javascript 复制代码
function existCircular(obj) {
  let cache = new Set(); 
  function helper(obj) {
    let values = Object.values(obj);
    for (let i = 0; i < values.length; i++) {
      if (cache.has(values[i])) {
        return true;
      }
      
      // 不是引用数据类型,直接跳过
      if(typeof values[i] !== 'object' || values[i] === null) continue
      cache.add(values[i]);
      
      let flag = helper(values[i]);
      // 如果 flag 是 false, 那么继续遍历,如果是 true,说明已经存在环了, 直接 return true
      if (flag) {
        return true;
      }
    }
    return false;
  }

  return helper(obj);
}

// 测试
const person = { name: 'kalory', age:18}
person.onwer = person

existCircular(person) // true

2.3 BFS实现

javascript 复制代码
const existCircularBFS = (obj) => {
  let cache = new Set();
  let values = [obj];

  while(values.length) {
    const item =  values.shift()
    if (cache.has(item)) {
        return true;
    }
    // 基本数据类型跳过
    if(typeof item !== 'object' || item === null) continue
    cache.add(item);
    // 主要这里 Object.values 拿到的是一个数组,我们需要展开push到values
    // 如果直接 push Object.values(item) 会造成死循话
    values.push(...Object.values(item))
  }
  
  return false;
}

好啦!今天的碎碎念就到这里啦。对你如果有帮助点个关注哦~🧸。

相关推荐
东东2334 分钟前
前端开发中如何取消Promise操作
前端·javascript·promise
掘金安东尼9 分钟前
官方:什么是 Vite+?
前端·javascript·vue.js
柒崽10 分钟前
ios移动端浏览器,vh高度和页面实际高度不匹配的解决方案
前端
烛阴35 分钟前
为什么游戏开发者都爱 Lua?零基础快速上手指南
前端·lua
大猫会长44 分钟前
tailwindcss出现could not determine executable to run
前端·tailwindcss
Moonbit1 小时前
MoonBit Pearls Vol.10:prettyprinter:使用函数组合解决结构化数据打印问题
前端·后端·程序员
533_1 小时前
[css] border 渐变
前端·css
云中雾丽1 小时前
flutter的dart语言和JavaScript的消息循环机制的异同
前端
地方地方1 小时前
Vue依赖注入:provide/inject 问题解析与最佳实践
前端·javascript·面试
云中雾丽1 小时前
dart的继承和消息循环机制
前端