谈谈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 相关文章推荐
如何在标题栏显示框架内页面的标题
Feb 03 Javascript
jQuery的12招常用技巧分享
Aug 08 Javascript
jquery.cookie.js使用指南
Jan 05 Javascript
纯Javascript实现ping功能的方法
Mar 20 Javascript
smartupload实现文件上传时获取表单数据(推荐)
Dec 12 Javascript
Vue自定义事件(详解)
Aug 19 Javascript
vue-cli启动本地服务局域网不能访问的原因分析
Jan 22 Javascript
JS实现的集合去重,交集,并集,差集功能示例
Mar 13 Javascript
为什么使用koa2搭建微信第三方公众平台的原因
May 16 Javascript
基于vue实现一个禅道主页拖拽效果
May 27 Javascript
移动端底部导航固定配合vue-router实现组件切换功能
Jun 13 Javascript
原型和原型链 prototype和proto的区别详情
Nov 02 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
isset和empty的区别
2007/01/15 PHP
慎用preg_replace危险的/e修饰符(一句话后门常用)
2013/06/19 PHP
PHP 下载文件时自动添加bom头的方法实例
2014/01/10 PHP
使用PHP如何实现高效安全的ftp服务器(一)
2015/12/20 PHP
laravel validate 设置为中文的例子(验证提示为中文)
2019/09/29 PHP
在表单提交前进行验证的几种方式整理
2013/07/31 Javascript
jquery ajax的success回调函数中实现按钮置灰倒计时
2013/11/19 Javascript
js showModalDialog弹出窗口实例详解
2014/01/07 Javascript
jquery提交form表单时禁止重复提交的方法
2014/02/13 Javascript
JavaScript编程中实现对象封装特性的实例讲解
2016/06/24 Javascript
JS实现六位字符密码输入器功能
2016/08/19 Javascript
js实现可输入可选择的select下拉框
2016/12/21 Javascript
JSON字符串和JSON对象相互转化实例详解
2017/01/05 Javascript
浅谈vue项目可以从哪些方面进行优化
2018/05/05 Javascript
独立部署小程序基于nodejs的服务器过程详解
2019/06/24 NodeJs
Vue实现商品分类菜单数量提示功能
2019/07/26 Javascript
Vue 同步异步存值取值实现案例
2020/08/05 Javascript
Python matplotlib画图实例之绘制拥有彩条的图表
2017/12/28 Python
Python中如何导入类示例详解
2019/04/17 Python
python实现读取excel文件中所有sheet操作示例
2019/08/09 Python
Python3.7安装keras和TensorFlow的教程图解
2020/06/18 Python
在HTML5中使用MathML数学公式的简单讲解
2016/02/19 HTML / CSS
印尼太阳百货公司网站:Matahari
2018/02/04 全球购物
采购部岗位职责
2013/11/24 职场文书
京剧自荐信
2014/01/26 职场文书
信息学院毕业生自荐信范文
2014/03/04 职场文书
安全生产责任书
2014/03/12 职场文书
银行竞聘演讲稿范文
2014/04/23 职场文书
大三学习计划书范文
2014/05/02 职场文书
讲解员培训方案
2014/05/04 职场文书
2014年幼儿园老师工作总结
2014/12/05 职场文书
2014年医务科工作总结
2014/12/18 职场文书
2015年清剿火患专项行动工作总结
2015/07/27 职场文书
如何拟写通知正文?
2019/04/02 职场文书
关于SpringBoot 使用 Redis 分布式锁解决并发问题
2021/11/17 Redis
方法汇总:Python 安装第三方库常用
2022/04/26 Python