最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 聊一聊 JS 中的循环引用及问题

    正文概述 掘金(codingOrange)   2020-12-10   112

    本文主要从 JS 中为什么会出现循环引用,垃圾回收策略中引用计数为什么有很大的问题,以及循环引用时的对象在使用 JSON.stringify 时为什么会报错,怎样解决这个问题简单谈谈自己的一些理解。

    1. 什么是循环引用

    当对象 1 中的某个属性指向对象 2,对象 2 中的某个属性指向对象 1 就会出现循环引用,(当然不止这一种情况,不过原理是一样的)下面通过代码和内存示意图来说明一下。

    function circularReference() {
      let obj1 = {
    	};
    	let obj2 = {
     	 b: obj1
    	};
    	obj1.a = obj2;
    }
    

    上述代码在内存中的示意图

    聊一聊 JS 中的循环引用及问题

    从上图可以看出 obj1 中的 a 属性引用 obj2,obj2 中的 b 属性引用 obj1,这样就构成了循环引用。

    2.JS 中引用计数垃圾回收策略的问题

    先简单讲一下 JS 中引用垃圾回收策略大体是什么样的一个原理,当一个变量被赋予一个引用类型的值时,这个引用类型的值的引用计数加 1。就像是代码中的 obj1 这个变量被赋予了 obj1 这个对象的地址,obj1 这个变量就指向了这个 obj1(右上)这个对象,obj1(右上)的引用计数就会加1.当变量 obj1的值不再是 obj1(右上)这个对象的地址时,obj1(右上)这个对象的引用计数就会减1.当这个 obj1(右上)对象的引用计数变成 0 后,垃圾收集器就会将其回收,因为此时没有变量指向你,也就没办法使用你了。

    看似很合理的垃圾回收策略为什么会有问题呢?

    就是上面讲到的循环引用导致的,下面来分析一下。当 obj1 这个变量执行 obj1 这个对象时,obj1 这个对象的引用计数会加 1,此时引用计数值为 1,接下来 obj2 的 b 属性又指向了 obj1 这个对象,所以此时 obj1 这个对象的引用计数为 2。同理 obj2 这个对象的引用计数也为2.

    当代码执行完后,会将变量 obj1 和 obj2 赋值为 null,但是此时 obj1 和 obj2 这两个对象的引用计数都为1,并不为 0,所以并不会进行垃圾回收,但是这两个对象已经没有作用了,在函数外部也不可能使用到它们,所以这就造成了内存泄露。

    在现在广泛采用的标记清除回收策略中就不会出现上面的问题,标记清除回收策略的大致流程是这样的,最开始的时候将所有的变量加上标记,当执行 cycularReference 函数的时候会将函数内部的变量这些标记清除,在函数执行完后再加上标记。这些被清除标记又被加上标记的变量就被视为将要删除的变量,原因是这些函数中的变量已经无法被访问到了。像上述代码中的 obj1 和 obj2 这两个变量在刚开始时有标记,进入函数后被清除标记,然后函数执行完后又被加上标记被视为将要清除的变量,因此不会出现引用计数中出现的问题,因为标记清除并不会关心引用的次数是多少。

    3. 循环引用的对象使用 JSON.stringify 为什么会报错

    JSON.stringify 用于将一个 JS 对象序列化为一个 JSON 字符串,假设现在我们要将 obj1 这个对象序列化为 JSON 字符串,现在我们先将 obj1 这个对象打印出来看一下。

    function circularReference() {
      let obj1 = {
    	};
    	let obj2 = {
     	 b: obj1
    	};
    	obj1.a = obj2;
      console.log(obj1);
    }
    circularReference();
    

    结果如下所示:

    聊一聊 JS 中的循环引用及问题

    obj1 这个对象和 obj2 会无限相互引用,JSON.tostringify 无法将一个无限引用的对象序列化为 JOSN 字符串。

    下面是 MDN 的解释:

    我们可以从加粗的字体中看到,对包含循环引用的对象执行 JSON.stringify,会抛出错误。

    聊一聊 JS 中的循环引用及问题

    解决方法

    一个自然的想法能不能消除循环引用,一个 JSON 扩展包 做到了这一点, 使用 JSON.decycle 可以去除循环引用。为了方便测试我直接在 JSON 扩展包的 Github 仓库中下载了 cycle.js 这个函数,将下面这段代码赋值到最下面,然后利用 node 运行进行测试,问题得到解决,结果如下图所示。

    function circularReference() {
    
      let obj1 = {
      };
      let obj2 = {
        b: obj1
      };
      obj1.a = obj2;
      let c = JSON.decycle(obj1);
      console.log(JSON.stringify(c));
    }
    circularReference();
    

    运行结果 聊一聊 JS 中的循环引用及问题


    起源地 » 聊一聊 JS 中的循环引用及问题

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元