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是弱引用,如果不在使用,空间会直接释放
上一篇下一篇

猜你喜欢

热点阅读