简单聊一聊JS中的循环引用及问题
本文主要从 JS 中为什么会出现循环引用,垃圾回收策略中引用计数为什么有很大的问题,以及循环引用时的对象在使用 JSON.stringify 时为什么会报错,怎样解决这个问题简单谈谈自己的一些理解。
1. 什么是循环引用
当对象 1 中的某个属性指向对象 2,对象 2 中的某个属性指向对象 1 就会出现循环引用,(当然不止这一种情况,不过原理是一样的)下面通过代码和内存示意图来说明一下。
function circularReference() {
let obj1 = {
};
let obj2 = {
b: obj1
};
obj1.a = obj2;
}
上述代码在内存中的示意图
从上图可以看出 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();
结果如下所示:
obj1 这个对象和 obj2 会无限相互引用,JSON.tostringify 无法将一个无限引用的对象序列化为 JOSN 字符串。
下面是 MDN 的解释:
JSON.stringify() 将值转换为相应的JSON格式:
- 转换值如果有 toJSON() 方法,该方法定义什么值将被序列化。
- 非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。
- 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。
undefined
、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成null
(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined,如JSON.stringify(function(){})
orJSON.stringify(undefined)
.- 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
- 所有以 symbol 为属性键的属性都会被完全忽略掉,即便
replacer
参数中强制指定包含了它们。- Date 日期调用了 toJSON() 将其转换为了 string 字符串(同Date.toISOString()),因此会被当做字符串处理。
- NaN 和 Infinity 格式的数值及 null 都会被当做 null。
- 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性。
我们可以从加粗的字体中看到,对包含循环引用的对象执行 JSON.stringify,会抛出错误。
解决方法
一个自然的想法能不能消除循环引用,一个 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中的循环语句
js中的循环语句可分为三种:1.while:2.do……while:3.for. while的语法为 while (exp) { //statements;} var a=1,b=0; whil ...
- js中的循环
js中的循环是我们经常要用到的,现在进行一些归纳. 一.javascript种的循环. 1.循环对象 var o = { name: 'Jack', age: 20, city: 'Beijing' ...
- js中for循环的研究
转自:http://blog.csdn.net/lushuaiyin/article/details/8541500 <html> <body> <b><ce ...
- java和js中for循环的区别
java中for循环,先执行语句后循环 for (int i=1;i<10;i++){ for(int b=1;b<=i;b++){ System.out.print(b+"*& ...
- JS中的循环嵌套 BOM函数
[嵌套循环特点] 外层循环转一次,内层循环转一圈 外层循环控制行数,内层循环控制每行元素个数 [做 ...
- 如何在 iOS 中解决循环引用的问题
稍有常识的人都知道在 iOS 开发时,我们经常会遇到循环引用的问题,比如两个强指针相互引用,但是这种简单的情况作为稍有经验的开发者都会轻松地查找出来. 但是遇到下面这样的情况,如果只看其实现代码,也很 ...
- JS中for循环变量作用域--解决for循环异步执行的问题
被这个问题困惑了很久,终于在网上找到了答案,感谢~ 现在分享给大家~ js中如何让一个for循环走完之后,再去执行下面的语句? 这涉及for循环变量作用域的问题,js中作用域只有函数作用域和全局作用域 ...
- 【FastJSON】解决FastJson中“$ref 循环引用”的问题
0.开发环境 SSH,EasyUI,MySQL 1.需求要求: (1)首先获取所有的贷款订单数据,即List <LoanOrder>. (2)然后从单个贷款订单实体LoanOrder去访问 ...
- swift闭包中解决循环引用的问题
swift中可以通过三种方法解决循环引用的问题 利用类似oc方法解决循环引用weak var weakSelf = self weak var weakSelf = self loadData = { ...
随机推荐
- HDU3746 Teacher YYF 题解 KMP算法
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3746 题目大意:给你一个串 \(s\) ,要求 \(s\) 的开头或结尾添加最少的字符,使得添加后的串 ...
- Hibernate懒加载导致json数据对象传输异常的问题---(非常重要)
1. 异常: [console_demo][WARN] [2016-12-15 19:49:35] org.springframework.web.servlet.mvc.support.Defaul ...
- supersockets和 AppSession,AppServer 配合工作
现在, 你已经有了 RequestInfo, ReceiveFilter 和 ReceiveFilterFactory, 但是你还没有正式使用它们. 如果你想让他们在你的程序里面可用, 你需要定义你们 ...
- HDU 1850 Nim-Sum思想总结、
算法介绍: Nim游戏是指两个对手在m个堆中轮流随意从某一个堆中拿出n个元素,假定两个对手都是足够聪明,直至最后一次取的人将所有元素取出,此人取得胜利.与之相反的是Misere游戏,相同的游戏规则,但 ...
- 3-7 彻底搞清楚unicode和utf8编码
- java 面试题之交通灯管理系统
需求: 交通灯管理系统的项目需求 Ø 异步随机生成按照各个路线行驶的车辆. 例如: 由南向而来去往北向的车辆 ---- 直行车辆 由西向而来去往南向的车辆 ---- 右转车辆 由东向而来去往南向的车辆 ...
- P1030 队列的基本操作
题目描述 现在给你一个队列,它一开始是空的,你需要模拟队列的操作.队列的操作包括如下: "push x":将元素 x 放入队列中,其中x是一个int范围内的整数: "po ...
- P1009 字符三角形
题目描述 输入一个字符c,按照阳历输出的格式输出由该字符组成的一个字符三角形. 输入格式 输入包含一个字符c. 输出格式 输出由该字符c组成的字符三角形. 样例输入 A 样例输出 A AAA AAAA ...
- 【23.68%】【hdu 2871】Memory Control
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission ...
- ASP.NET MVC 实现页落网资源分享网站+充值管理+后台管理(3)之创建实体层
实体层是介于表现层和业务层之间,同时也作为数据载体贯穿了整个项目之间的数据传递,创建实体有很多方法,我们可以手工创建,也可以代码生成引擎等等,我们这里主要应用数据实体模型连接生成: 创建好之后,我们需 ...