谈谈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 相关文章推荐
防止网站内容被拷贝的一些方法与优缺点好处与坏处分析
Nov 30 Javascript
Javascript this指针
Jul 30 Javascript
JavaScript判断访问的来源是手机还是电脑,用的哪种浏览器
Dec 12 Javascript
JS 屏蔽按键效果与改变按键效果的示例代码
Dec 24 Javascript
js实现touch移动触屏滑动事件
Apr 17 Javascript
jquery SweetAlert插件实现响应式提示框
Aug 18 Javascript
jQuery属性选择器用法示例
Sep 09 Javascript
Angular的事件和表单详解
Dec 26 Javascript
Bootstrap Table使用整理(五)之分页组合查询
Jun 09 Javascript
JS字符串去除连续或全部重复字符的实例
Mar 08 Javascript
JQuery中queue方法用法示例
Jan 31 jQuery
JQuery animate动画应用示例
May 14 jQuery
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
非洲第一个咖啡超凡杯大赛承办国—卢旺达的咖啡怎么样
2021/03/03 咖啡文化
Zend Studio去除编辑器的语法警告设置方法
2012/10/24 PHP
php设计模式之命令模式使用示例
2014/03/02 PHP
php对数组内元素进行随机调换的方法
2015/05/12 PHP
php数字每三位加逗号的功能函数
2015/10/22 PHP
PHP编程实现脚本异步执行的方法
2017/08/09 PHP
PHP常用工具函数小结【移除XSS攻击、UTF8与GBK编码转换等】
2019/04/27 PHP
解决php写入数据库乱码的问题
2019/09/17 PHP
JS获取dom 对象 ajax操作 读写cookie函数
2009/11/18 Javascript
js 强制弹出窗口代码研究-又一款代码
2010/03/20 Javascript
javascript权威指南 学习笔记之javascript数据类型
2011/09/24 Javascript
文字溢出实现溢出的部分再放入一个新生成的div中具体代码
2013/05/17 Javascript
js判断背景图片是否加载成功使用img的width实现
2013/05/29 Javascript
JS失效 提示HTML1114: (UNICODE 字节顺序标记)的代码页 utf-8 覆盖(META 标记)的冲突的代码页 utf-8
2017/06/23 Javascript
vue vuex vue-rouert后台项目——权限路由(适合初学)
2017/12/29 Javascript
从零开始在NPM上发布一个Vue组件的方法步骤
2018/12/20 Javascript
利用Promise自定义一个GET请求的函数示例代码
2019/03/20 Javascript
three.js显示中文字体与tween应用详析
2021/01/04 Javascript
详解ES6 中的Object.assign()的用法实例代码
2021/01/11 Javascript
JavaScript 判断浏览器是否是IE
2021/02/19 Javascript
[00:32]2018DOTA2亚洲邀请赛Liquid出场
2018/04/03 DOTA
实例讲解python函数式编程
2014/06/09 Python
使用Turtle画正螺旋线的方法
2017/09/22 Python
Python填充任意颜色,不同算法时间差异分析说明
2020/05/16 Python
Python3安装模块报错Microsoft Visual C++ 14.0 is required的解决方法
2020/07/28 Python
H5离线存储Manifest原理及使用
2020/04/28 HTML / CSS
学生党员思想汇报范文
2014/01/09 职场文书
认购协议书范本
2014/04/22 职场文书
2014年秋季开学演讲稿
2014/05/24 职场文书
博士生导师推荐信
2014/07/08 职场文书
锅炉工岗位职责
2015/02/13 职场文书
护理工作个人总结
2015/03/03 职场文书
政协委员个人总结
2015/03/03 职场文书
小学语文教学随笔
2015/08/14 职场文书
Django程序的优化技巧
2021/04/29 Python
世界十大狙击步枪排行榜
2022/03/20 杂记