js对象的循环引用
2022-06-24 本文已影响0人
skoll
问题简述
1 .当一个对象的属性是自己的时候,使用JSON.stringify或者一般的没有处理的deepClone的时候会发现栈溢出
//如下代码
var b = {
}
var a = {
b: b
}
b.a = a
console.log(a)
image.png
常见情况
1 .json序列化的时候,序列化的对象尽量避免循环依赖,子类不定义与父类相同名称的成员,避免定义非成员变量的getter、setter方法
确认问题
1 .出现循环引用的时候算是bug么?要怎么处理
2 .webpack里面也会有这种情况,循环依赖a-b-c-a
1 .根据module ID,到installedMoodules里面去找之前有没有加载过,如果有加载过,就直接返回上次的
2 .如果没有,就新建一个,并且赋值给installedModules[moduled]
3 .对象存在循环引用的时候,打印不会出现栈溢出,深拷贝的时候,才会的导致栈溢出
如何判断一个对象出现了循环引用
1 .定义了引用类型的变量后该变量存的是堆内存的地址,通过地址访问堆内存的数据,从而产生了引用。而基本数据类型定义后存储的是数据值,不需要引用
2 .在js中对两个引用类型使用 === 判断是对两者的地址进行判断
3 .所以判断是否存在循环引用,可以简单定义为对象内部的属性是否和对象本身的地址相同
var obj = {
a: 1,
}
obj.c = obj
obj.b = obj
const keyMap = new Map();
// keyMap.set(obj, "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(obj))
//方法2
function cycle(obj, parent) {
//表示调用的父级数组
var parentArr = parent || [obj];
for (var i in obj) {
if (typeof obj[i] === "object") {
//判断是否有循环引用
parentArr.forEach((pObj) => {
if (pObj === obj[i]) {
obj[i] = "[cycle]"
}
});
cycle(obj[i], [...parentArr, obj[i]])
}
}
return obj;
}
处理方法
1 .去除对象中涉及到的循环引用的属性.消除循环引用JSON.decycle方法可以解除循环
{"a":{"b":{"$ref":"$"}}}
2 .将循环引用中的一个对象缓存起来,以避免重复序列化或者创建
1 .WeakMap来记录对象是否被克隆,主要考虑一下三点。
2 .WeakMap对象是key=>value形式,不会重复记录
3 .WeakMap是弱引用,如果不在使用,空间会直接释放