前言
将循环引用和JSON.stringify放在一起,眼界宽的大佬应该知道我要阐述的东西是什么了;不太清楚的xdm不用着急,这篇文章将带你们彻底搞懂循环引用,以及使用JSON.stringify的黑魔法来序列化循环引用的对象。
如有错误之处,还望指出!
一、What is 循环引用?
- 来自百度百科的解答
当一个单元格内的公式直接或间接地应用了这个公式本身所在的单元格时,就称为循环引用。
- 以上回答有点抽象,不过很简洁的表达了循环引用,接下来会站在js的角度去讲述什么是循环引用。
- 对象之间相互引用;
- 对象的属性引用对象本身;
举个栗子:
// 对象之间相互引用
let obj = { key: 2 }
let objTwins = { key: 5 }
obj.child = objTwins;
objTwins.child = obj;
// 对象的属性引用对象本身
let obj = { key: 2 }
obj.child = obj;
看到这里应该就大致明白什么是循环引用了吧!
二、判断对象为循环引用
知道什么是循环引用,那我们应该怎么去判断它呢?
思路: 从上面讲述的什么是循环引用,我们可以明显的看出符合循环引用的对象特有的标志 ---- 对象中的某个属性永远引用对象本身。所以利用ES6的Set类型,将对象放置Set中,然后遍历对象的每个属性使用Set中.has() 来判断是否已存在相同的值,如果存在就满足了循环引用的特征,这个对象就是循环引用。
代码如下:
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
}
参考:https://juejin.cn/post/7023898521010962462
三、如何在JSON.stringify中输出有循环引用的对象
- 引入该问题的原因,如下
由于循环引用,导致不能序列化。
解决思路: 不序列化循环引用的元素。
JSON.stringify的黑魔法
其实也就是我们基本不会用的JSON.stringify的第二个参数,它可以是一个过滤函数,也可以是一个数组,用来存放可序列化的属性值。
其中cache也可以
var cache = [];
var str = JSON.stringify(o, function(key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
// 移除
return;
}
// 收集所有的值
cache.push(value);
}
return value;
});
cache = null; // 清空变量,便于垃圾回收机制回收
更多关于JSON.stringify的知识,可以观看这位大佬