a.x=a={n:1} JS中使用连等赋值操作
来源:互联网 发布:俄罗斯啄木鸟 知乎 编辑:程序博客网 时间:2024/06/04 19:37
本文转载自 小小沧海博客
文章标题这句话原本是在国外某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!!! undefinedx;//undefined
可以看到只有obj输出了“getter!!!”,而x没有输出,用此特性来测试。
连等赋值测试2:
Object.defineProperty(window,"obj",{ get:function(){ console.log("getter!!!"); }});a=b=obj;//getter!!! undefined
通过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了
上述过程按序号图示:
按照上述过程可以看出旧的a.x和新的a都指向新创建的对象{n:2},所以他们应该是全等的。
测试:
var a = {n:1};var b = a;a.x = a = {n:2};console.log(a===b.x); //true
因为我们增加了var b=a,即将原对象增加了一条引用,所以在上述第5步时不会被释放,证实了上面的结论。
后记
通过这次了解了连续赋值的特点,再回过头看文章标题,似乎应该叫:
尽量不要使用JS的连续赋值操作,除非真的了解它的内部机制及可能会产生的后果。
原文链接-http://www.cnblogs.com/xxcanghai/p/4998076.h
- a.x=a={n:1} JS中使用连等赋值操作
- JS中连等赋值操作使用注意事项(a=b=c)
- JS中连等赋值操作使用注意事项(a=b=c)
- 不要在JS中使用连等赋值操作
- 连续赋值与求值顺序var a = {n:1};a.x = a = {n:2}; alert(a.x);
- 连续赋值与求值顺序var a = {n:1};a.x = a = {n:2}; alert(a.x)
- 连续赋值与求值顺序var a = {n:1};a.x = a = {n:2}; alert(a.x); // --> undefined
- JS连等赋值
- JS连等赋值
- x = power(a , (n -1)>>1);
- a(n+2)-4a(n+1)+4a(n) = 0 现在知道a(0) = x, a(10^8) = y, 求a((10^8)/2)
- a.x = a = { }, 深入理解赋值表达式
- 关于js连等赋值
- 据说是腾讯的面试题:两个数组a[N],b[N],其中A[N]的各个元素值已知,现给b[i]赋值,b[i] = a[0]*a[1]*a[2]...*a[N-1]/a[i];
- 两个数组a[N],b[N],其中A[N]的各个元素值已知,现给b[i]赋值,b[i] = a[0]*a[1]*a[2]...*a[N-1]/a[i];
- 两个数组a[N],b[N],其中A[N]的各个元素值已知,现给b[i]赋值,b[i] = a[0]*a[1]*a[2]…*a[N-1]/a[i];
- 两个数组a[N],b[N],其中A[N]的各个元素值已知,现给b[i]赋值,b[i] = a[0]*a[1]*a[2]…*a[N-1]/a[i]
- C语言printf("a=%#x\n",a)语句解析
- 已发货未发货
- 解决ubuntu下定时任务不执行问题
- 照片的虚化处理
- android中用Fresco实现圆角图片和圆形图片
- TP5分页丢失参数、TP5分页绑定参数
- a.x=a={n:1} JS中使用连等赋值操作
- 监听URL内容的JS交互-与解析URL内的参数
- 最简单侧滑退出activity的实现
- git
- Recyclerview多条目加载
- session和ip的对应关系
- 移动磁盘磁盘结构损坏且无法读取要如何办啊
- Angular2-使用Angular CLI快速搭建工程
- OpenGL入门学习