谈谈javascript中使用连等赋值操作带来的问题


Posted in Javascript onNovember 26, 2015

前言

文章标题这句话原本是在国外某JavaScript规范里看到的,当时并没有引起足够的重视,直到最近一次出现了bug发现JS里的连等赋值操作的特色(坑)。

网上搜索一番发现一个非常好的连等赋值的(来源1,来源2)例子:

var a = {n:1};
a.x = a = {n:2};
console.log(a.x); // 输出?

答案是:

console.log(a.x); // undefined

不知道各位有没有答对,至少我是答错了。

遂借此机会好好看看JS连等赋值是怎么回事

赋值顺序?

假设有一句代码: A=B=C; ,赋值语句的执行顺序是从右至左,所以问题在于:

是猜想1: B = C; A = C; ?

还是猜想2: B = C; A = B;  ?

我们都知道若两个对象同时指向一个对象,那么对这个对象的修改是同步的,如:

var a={n:1};
var b=a;
a.n=2;
console.log(b);//Object {n: 2}

所以可以根据这个特性来测试连续赋值的顺序。

按照猜想1,把C换成具体的对象,可以看到对a的修改不会同步到b上,因为在执行第一行和第二行时分别创建了两个 {n:1} 对象。如:

var b={n:1};
var a={n:1};
a.n=0;
console.log(b);//Object {n: 1}

再按照猜想2,把C换成具体的对象,可以看到对a的修改同步到了b,因为a和b同时引用了一个对象,如:

var b={n:1};
var a=b;
a.n=0;
console.log(b);//Object {n: 0}

测试真正的连等赋值:

var a,b;
a=b={n:1};
a.n=0;
console.log(b);//Object {n: 0}

可以看到是符合猜想2的,如果有人觉得这个测试不准确可以再来测试,使用ECMA5的setter和getter特性来测试。

首先setter和getter是应用于变量名的,而不是变量真正储存的对象,如下:

Object.defineProperty(window,"obj",{
 get:function(){
  console.log("getter!!!");
 }
});
var x=obj;
obj;//getter!!! undefined
x;//undefined

可以看到只有obj输出了“getter!!!”,而x没有输出,用此特性来测试。

连等赋值测试2:

Object.defineProperty(window,"obj",{
 get:function(){
  console.log("getter!!!");
 }
});
a=b=obj;//getter!!! undefined

谈谈javascript中使用连等赋值操作带来的问题

通过getter再次证实,在A=B=C中,C只被读取了一次。

所以,连等赋值真正的运算规则是  B = C; A = B;  即连续赋值是从右至左永远只取等号右边的表达式结果赋值到等号左侧。

连续赋值能拆开写么?

通过上面可以看到连续赋值的真正规则,那么再回归到文章开头的那个案例,如果按照上述规则将连续赋值拆开会发现结果不一样了,如:

var a={n:1};
a={n:2};
a.x=a;
console.log(a.x);//Object {n: 2, x: Object}

所以连续赋值语句虽然是遵从从右至左依次赋值的规则但依然不能将语句拆开来写,至于为什么

我猜测:js内部为了保证赋值语句的正确,会在一条赋值语句执行前,先把所有要赋值的引用地址取出一个副本,再依次赋值。

所以我认为这段代码 a.x=a={n:2}; 的逻辑是:

1、在执行前,会先将a和a.x中的a的引用地址都取出来,此值他们都指向{n:1}

2、在内存中创建一个新对象{n:2}

3、执行a={n:2},将a的引用从指向{n:1}改为指向新的{n:2}

4、执行a.x=a,此时a已经指向了新对象,而a.x因为在执行前保留了原引用,所以a.x的a依然指向原先的{n:1}对象,所以给原对象新增一个属性x,内容为{n:2}也就是现在a

5、语句执行结束,原对象由{n:1}变成{n:1,x:{n:2}},而原对象因为无人再引用他,所以被GC回收,当前a指向新对象{n:2}

6、所以就有了文章开头的运行结果,再执行a.x,自然就是undefined了

上述过程按序号图示:

谈谈javascript中使用连等赋值操作带来的问题

谈谈javascript中使用连等赋值操作带来的问题

按照上述过程可以看出旧的a.x和新的a都指向新创建的对象{n:2},所以他们应该是全等的。

测试:

var a = {n:1};
var b = a;
a.x = a = {n:2};
console.log(a===b.x); //true

谈谈javascript中使用连等赋值操作带来的问题

因为我们增加了var b=a,即将原对象增加了一条引用,所以在上述第5步时不会被释放,证实了上面的结论。

后记

通过这次了解了连续赋值的特点,再回过头看文章标题,似乎应该叫:

尽量不要使用JS的连续赋值操作,除非真的了解它的内部机制及可能会产生的后果。

Javascript 相关文章推荐
Domino中运用jQuery读取视图内容的方法
Oct 21 Javascript
兼容IE与firefox火狐的回车事件(js与jquery)
Oct 20 Javascript
JavaScript实现的购物车效果可以运用在好多地方
May 09 Javascript
Enter回车切换输入焦点实现思路与代码兼容各大浏览器
Sep 01 Javascript
基于jQuery实现响应式圆形图片轮播特效
Nov 25 Javascript
BootStrap的弹出框(Popover)支持鼠标移到弹出层上弹窗层不隐藏的原因及解决办法
Apr 03 Javascript
JavaScript生成.xls文件的代码
Dec 22 Javascript
详解JS中遍历语法的比较
Apr 07 Javascript
bootstrap table实现x-editable的行单元格编辑及解决数据Empty和支持多样式问题
Aug 10 Javascript
JavaScript实现的原生态Tab标签页功能【兼容IE6】
Sep 18 Javascript
关于RxJS Subject的学习笔记
Dec 05 Javascript
JS操作json对象key、value的常用方法分析
Oct 29 Javascript
jQuery实现的AJAX简单弹出层效果代码
Nov 26 #Javascript
js检测iframe是否加载完成的方法
Nov 26 #Javascript
学习JavaScript设计模式(策略模式)
Nov 26 #Javascript
深入浅析同源策略和跨域访问
Nov 26 #Javascript
js实现iframe框架取值的方法(兼容IE,firefox,chrome等)
Nov 26 #Javascript
学习JavaScript设计模式(单例模式)
Nov 26 #Javascript
javascript bom是什么及bom和dom的区别
Nov 26 #Javascript
You might like
ajax在joomla中的原生态应用代码
2012/07/19 PHP
PHP设计模式之调解者模式的深入解析
2013/06/13 PHP
深入理解PHP中的Streams工具
2015/07/03 PHP
ThinkPHP中html:list标签用法分析
2016/01/09 PHP
thinkphp表单上传文件并将文件路径保存到数据库中
2016/07/28 PHP
有关PHP 中 config.m4 的探索
2020/08/26 PHP
玩转方法:call和apply
2014/05/08 Javascript
推荐10个2014年最佳的jQuery视频插件
2014/11/12 Javascript
JavaScript中setMonth()方法的使用详解
2015/06/11 Javascript
JS模拟简易滚动条效果代码(附demo源码)
2016/04/05 Javascript
vue router路由嵌套不显示问题的解决方法
2017/06/17 Javascript
AngularJS中的路由使用及实现代码
2017/10/09 Javascript
Servlet3.0与纯javascript通过Ajax交互的实例详解
2018/03/18 Javascript
微信小程序之自定义组件的实现代码(附源码)
2018/08/02 Javascript
ES6的异步终极解决方案分享
2019/07/11 Javascript
微信小程序 授权登录详解(附完整源码)
2019/08/23 Javascript
Python使用random和tertools模块解一些经典概率问题
2015/01/28 Python
Python3实现转换Image图片格式
2018/06/21 Python
Python+OpenCV目标跟踪实现基本的运动检测
2018/07/10 Python
django 实现电子支付功能的示例代码
2018/07/25 Python
Python 限制线程的最大数量的方法(Semaphore)
2019/02/22 Python
python matplotlib画图库学习绘制常用的图
2019/03/19 Python
浅析PyTorch中nn.Linear的使用
2019/08/18 Python
零基础学Python之前需要学c语言吗
2020/07/21 Python
通过代码实例了解Python3编程技巧
2020/10/13 Python
编辑硕士自荐信范文
2013/11/27 职场文书
酒店营销策划方案
2014/02/07 职场文书
合作协议书模板2014
2014/09/26 职场文书
银行职员工作失误检讨书
2014/10/14 职场文书
小学教学工作总结2015
2015/05/13 职场文书
消防验收申请报告
2015/05/15 职场文书
植树节新闻稿
2015/07/17 职场文书
高中军训感想
2015/08/07 职场文书
2019年12月24日平安夜祝福语集锦
2019/12/24 职场文书
vue基于Teleport实现Modal组件
2021/05/31 Vue.js
redis lua限流算法实现示例
2022/07/15 Redis