听说你的对象有个”环“?怎么发现的呢?

2022-07-25 08:23:57 浏览数 (2)

1. 手写62 方法学习JavaScript底层原理

判断一个对象是否存在循环引用已收录至 手写各种源码实现,也可以直接点击isCyclic快速查看,目前已有62 手写实现,欢迎一起来学习喔。

2. 不得不说的循环引用

如下图: 相信曾经你也到过类似的问题,循环引用。如果两个对象相互传递引用或者对象的属性引用其本身都有可能会造成循环引用。

在旧的浏览器中循环引用是造成内存泄漏的一个原因,当然随着垃圾收集算法的改进,现在可以很好地处理循环引用,这不再是一个问题。

只需要3分钟时间,本文会您一起学习

  1. 哪些情况可能会造成循环引用(重要)?
  2. 如何判断对象是否存在循环引用(重要)?

3. 出现循环引用的几种情况

常见的循环引用有两种情况,对象之间相互引用对象的属性引用对象本身

3.1 对象之间相互引用

代码语言:javascript复制
let obj1 = { name: '前端胖头鱼1' }
let obj2 = { name: '前端胖头鱼2' }
// 对象1的属性引用了对象2
obj1.obj = obj2
// 对象2的属性引用了对象1
obj2.obj = obj1

3.2 对象的属性引用对象本身

1. 直接引用最外层的对象

代码语言:javascript复制
let obj = { name: '前端胖头鱼1' }
// 对象的属性引用了对象本身
obj.child = obj

2. 引用对象的部分属性

代码语言:javascript复制

let obj = {
  name: '前端胖头鱼',
  child: {}
}

obj.child.obj = obj.child

4. 如何判断对象是否存在循环引用?

根据出现循环引用可能有的几种情况,我们可以试着写出下列代码

4.1 源码实现

代码语言:javascript复制
const isCyclic = (obj) => {
  // 使用Set数据类型来存储已经检测过的对象
  let stackSet = new Set()
  let detected = false

  const detect = (obj) => {
    // 不是对象类型的话,可以直接跳过
    if (obj && typeof obj != 'object') {
      return
    }
    // 当要检查的对象已经存在于stackSet中时,表示存在循环引用
    if (stackSet.has(obj)) {
      return detected = true
    }
    // 将当前obj存如stackSet
    stackSet.add(obj)

    for (let key in obj) {
      // 对obj下的属性进行挨个检测
      if (obj.hasOwnProperty(key)) {
        detect(obj[key])
      }
    }
    // 平级检测完成之后,将当前对象删除,防止误判
    /*
      例如:对象的属性指向同一引用,如果不删除的话,会被认为是循环引用
      let tempObj = {
        name: '前端胖头鱼'
      }
      let obj4 = {
        obj1: tempObj,
        obj2: tempObj
      }
    */
    stackSet.delete(obj)
  }

  detect(obj)

  return detected
}


4.2 测试一把

代码语言:javascript复制
// 1. 对象之间相互引用

let obj1 = { name: '前端胖头鱼1' }
let obj2 = { name: '前端胖头鱼2' }
// 对象1的属性引用了对象2
obj1.obj = obj2
// 对象2的属性引用了对象1
obj2.obj = obj1

console.log(isCyclic(obj1)) // true
console.log(isCyclic(obj2)) // true

// 2. 对象的属性引用了对象本身

let obj = { name: '前端胖头鱼1' }
// 对象的属性引用了对象本身
obj.child = obj

console.log(isCyclic(obj)) // true

// 3. 对象的属性引用部分属性

let obj3 = {
  name: '前端胖头鱼',
  child: {}
}

obj3.child.obj = obj3.child

console.log(isCyclic(obj3)) // true

// 4. 对象的属性指向同一引用
let tempObj = {
  name: '前端胖头鱼'
}
let obj4 = {
  obj1: tempObj,
  obj2: tempObj
}

console.log(isCyclic(obj4)) // false

// 5. 其他数据类型

console.log(isCyclic(1)) // false
console.log(isCyclic('前端胖头鱼')) // false
console.log(isCyclic(false)) // false
console.log(isCyclic(null)) // false
console.log(isCyclic(undefined)) // false
console.log(isCyclic([])) // false
console.log(isCyclic(Symbol('前端胖头鱼'))) // false

5. 结尾

一个非常小的知识点,感谢大家阅读。如果有兴趣可以更进一步探索一些有意思的话题:

比如:

  1. 如何在JSON.stringify中输出有循环引用的对象。
  2. JS的垃圾回收机制中是如何处理循环引用的等等。

0 人点赞