如何判断一个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;
}

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

相关推荐
hedley(●'◡'●)22 分钟前
基于cesium和vue的大疆司空模仿程序
前端·javascript·vue.js·python·typescript·无人机
qq5_81151751523 分钟前
web城乡居民基本医疗信息管理系统信息管理系统源码-SpringBoot后端+Vue前端+MySQL【可直接运行】
前端·vue.js·spring boot
百思可瑞教育24 分钟前
构建自己的Vue UI组件库:从设计到发布
前端·javascript·vue.js·ui·百思可瑞教育·北京百思教育
百锦再24 分钟前
Vue高阶知识:利用 defineModel 特性开发搜索组件组合
前端·vue.js·学习·flutter·typescript·前端框架
CappuccinoRose1 小时前
JavaScript 学习文档(二)
前端·javascript·学习·数据类型·运算符·箭头函数·变量声明
这儿有一堆花1 小时前
Vue 是什么:一套为「真实业务」而生的前端框架
前端·vue.js·前端框架
全栈前端老曹1 小时前
【MongoDB】深入研究副本集与高可用性——Replica Set 架构、故障转移、读写分离
前端·javascript·数据库·mongodb·架构·nosql·副本集
NCDS程序员2 小时前
v-model: /v-model/ :(v-bind)三者核心区别
前端·javascript·vue.js
夏幻灵2 小时前
CSS三大特性:层叠、继承与优先级解析
前端·css
小杨同学呀呀呀呀2 小时前
Ant Design Vue <a-timeline>时间轴组件失效解决方案
前端·javascript·vue.js·typescript·anti-design-vue