之前有一道面试题困惑我很久,后来在网上搜索了一下,貌似很多人都会进入误区,但到现在为止我还没有看到一个合理的解释这让我很郁闷!
面试题如下:
var foo={n:1}; var bar=foo; foo.x=foo={n:2}; console.log(foo.x) //输出?
答案是:
console.log(a.x); // undefined不知道各位有没有答对,至少我是答错了。 遂借此机会好好看看JS连等赋值是怎么回事;在网上查了很多的资料,最后发现两个非常好的联等赋值的栗子([栗子1](http://segmentfault.com/q/1010000002637728),[栗子2](http://www.iteye.com/topic/785445)),最后看了一下[小小沧海](https://www.cnblogs.com/xxcanghai/p/4998076.html?utm_source=tuicool&utm_medium=referral)的文章,觉得可以和大家分享下; 首先我们考虑一行代码:A=B=C;赋值语句的执行顺序是从右到左,所以我们猜想一下到底是:B=C;A=C;还是B=C;A=B; 我们都知道若两个对象同时指向一个对象,那么对这个对象的修改是同步的,如:
var a={n:1}; var b=a; a.n=2; console.log(b);//Object {n: 2}
所以可以根据这个特性来测试连续赋值的顺序。
首先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
通过getter证实,在A=B=C中,C只被读取了一次。
所以,连等赋值真正的运算规则是 B = C; A = B; 即连续赋值是从右至左永远只取等号右边的表达式结果赋值到等号左侧。
所以猜想这段代码 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了
测试:
var foo={n:1}; var bar=foo; foo.x=foo={n:2}; console.log(a===b.x);//true
因为我们增加了var bar=foo,即将原对象增加了一条引用,所以在上述第5步时不会被释放,证实了上面的结论。
结语
通过测试和小小沧海 的文章,我认为我还没有真正了解它内部的具体机制,文章中有些地方也是给出了猜想二字,所以我还是建议:尽量不要使用JS的连续赋值操作,除非真的了解它的内部机制及可能会产生的后果。