如何判断JS对象中是否存在循环引用

8,587

什么情况下会出现循环引用?

我个人的理解是,如果一个对象的值等于父级(祖父级,曾祖父级....),则说明是循环引用了。来看下面一个例子:

用上面代码来解释我定义的循环引用:a.info的值是a,而a恰好是a.info的父级,所以这里就是循环引用了。

如何处理循环引用对象呢?

首先,循环引用对象本来没有什么问题,序列化的时候才会发生问题,比如调用JSON.stringify()对该类对象进行序列化,就会报错: Converting circular structure to JSON.,而序列化需求很常见,比如发起一个ajax请求提交一个对象就需要对对象进行序列化。

针对上面这样的问题,可以通过JSON扩展包的var c = JSON.decycle(a)var a = JSON.retrocycle(c)来处理。这里就不做过多的解释了。

重点来了

手动写一个方法来判断对象是否存在循环引用,来看下边一段代码:

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;
}

上面函数中,parentArr为取值的所有父级的一个集合;

循环对象,如果对象是一个object类型的,先循环数组parentArr,判断该值是否是parentArr中的引用,如果是,则存在循环引用,并把循环引用值设置成特殊标识"[cycle]",接着再进行递归调用,并且把对应取值链上的父级集合传递下去。

测试:

var a = {
    b:{
        c:{}
    }
};

a.b.c.d = a;

console.log(cycle(a));

结果如下:

可以看到,a.b.c.d原本是一个循环引用的a值,经过处理以后,变成了循环引用的标识;

内容就这么多,有错误希望大佬可以多多指点,交流才能进步!