谈谈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 相关文章推荐
js 方法实现返回多个数据的代码
Apr 30 Javascript
基于jquery的分页控件(C#)
Jan 06 Javascript
js带按钮的提示框可供选择示例代码
Sep 17 Javascript
JavaScript监听文本框回车事件并过滤文本框空格的方法
Apr 16 Javascript
JS动态添加iframe的代码
Sep 14 Javascript
js模式化窗口问题![window.dialogArguments]
Oct 30 Javascript
jquery实现手机端单店铺购物车结算删除功能
Feb 22 Javascript
Vue组件化开发思考
Feb 02 Javascript
JS设计模式之观察者模式实现实时改变页面中金额数的方法
Feb 05 Javascript
r.js来合并压缩css文件的示例
Apr 26 Javascript
AngularJS实现与后台服务器进行交互的示例讲解
Aug 13 Javascript
Node.js API详解之 util模块用法实例分析
May 09 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
与空气斗智斗勇的经典《Overlord》,传说中的“无稽之谈”
2020/04/09 日漫
php 购物车实例(申精)
2009/05/11 PHP
coreseek 搜索英文的问题详解
2013/06/08 PHP
PHP获取表单所有复选框的值的方法
2014/08/28 PHP
thinkphp项目部署到Linux服务器上报错“模板不存在”如何解决
2016/04/27 PHP
PHP入门教程之PHP操作MySQL的方法分析
2016/09/11 PHP
Laravel框架中集成MongoDB和使用详解
2019/10/17 PHP
Yii2框架中一些折磨人的坑
2019/12/15 PHP
jquery绑定原理 简单解析与实现代码分享
2011/09/06 Javascript
获取元素距离浏览器周边的位置的方法getBoundingClientRect
2013/04/17 Javascript
Firefox中通过JavaScript复制数据到剪贴板(Copy to Clipboard 跨浏览器版)
2013/11/22 Javascript
PHP中使用微秒计算脚本执行时间例子
2014/11/19 Javascript
JS控制网页动态生成任意行列数表格的方法
2015/03/09 Javascript
ES6学习笔记之map、set与数组、对象的对比
2018/03/01 Javascript
微信小程序+云开发实现欢迎登录注册
2019/05/24 Javascript
vue封装可复用组件confirm,并绑定在vue原型上的示例
2019/10/31 Javascript
Windows系统下安装Python的SSH模块教程
2015/02/05 Python
使用Python编写一个在Linux下实现截图分享的脚本的教程
2015/04/24 Python
python创建临时文件夹的方法
2015/07/06 Python
python中WSGI是什么,Python应用WSGI详解
2017/11/24 Python
python生成tensorflow输入输出的图像格式的方法
2018/02/12 Python
如何使用Python的Requests包实现模拟登陆
2018/04/27 Python
python3对接mysql数据库实例详解
2019/04/30 Python
Python3实现定时任务的四种方式
2019/06/03 Python
Python 实现将大图切片成小图,将小图组合成大图的例子
2020/03/14 Python
Python 如何实现数据库表结构同步
2020/09/29 Python
想学画画?python满足你!
2020/12/24 Python
CSS3 实现的火焰动画
2020/12/07 HTML / CSS
HTML5实现分享到微信好友朋友圈QQ好友QQ空间微博二维码功能
2018/01/03 HTML / CSS
培训主管的岗位职责
2013/11/23 职场文书
行政助理岗位职责范文
2013/12/03 职场文书
2015年社区宣传工作总结
2015/05/20 职场文书
刑事附带民事诉讼答辩状
2015/05/22 职场文书
新闻通讯稿模板
2015/07/22 职场文书
公司会议开幕词
2016/03/03 职场文书
win10键盘驱动怎么修复?Win10键盘驱动修复小技巧
2022/04/06 数码科技