1、面试题
今天无意中在网上看到一道面试题,据说是阿里的(我也无从考证),题目如下
var a = {n: 1}
var b = a
a.x = a = {n: 2}
console.log(a.n, b.n)
console.log(a.x, b.x)
先说答案
a.n = 2
b.n = 1
a.x = undefined
b.x = {n: 2}
不出所料,我答错了,于是便上网找解析,看了好多文章,感觉猜想和说法都是五花八门,我也是捉摸了好久,才理解过来。
2、连等=执行顺序
假设有一句代码: 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
的
所以,连等赋值真正的运算规则是 B = C; A = B
,即连续赋值:是从右至左
永远只取等号右边的表达式结果赋值到等号左侧
3、关于面试题的猜想
3.1、猜想一
按照从右往左
顺序赋值考虑,对于a.x = a = {n:2}
,按常规思路应该是:
- 先把
{n:2}
赋值给a
- 然后再
创建 a.x
,将{n:2}
再赋值给a.x
按照这种说法,a.x
确实是被赋值为{n:2}
。可是事实上,a.x
的值却是 undefined
。
所以很明显,猜想一这种解法是有错误的
,我们不能忽视.
运算符和=
运算符的优先级问题
3.2、猜想二
注意:.
运算优先
于=
赋值运算
因此此处赋值可理解为:
- 声明
变量a
,a的指针
指向地址为{n:1}
- 声明
变量b
,b的指针
指向也为地址{n:1}
.
运算优先于=
赋值运算,所以先执行.
运算,声明a对象
中的x
属性,用于赋值,此时b仍然指向a的地址,同时拥有未赋值的x属性,a和b此时为{n:1 , x:undefined}
- 执行
=
赋值运算,对a对象赋值,此时变量名a
改变指向到对象{n:2}
- 执行
a.x=a
的操作,因为在第三步a.x
的指针已经创建,所以直接将a.x
的指针指向{n:2}
即可 - b的指针依旧指向原来地址,即
{n:1,x:{n:2}}
(控制台打印)
所以,猜想二这种解法是正确的
,同时在这个过程中,我们始终要明白:
在变量被赋值
,指针发生改变
的时候
- 如果
已有指针
,那么不改变它
- 如果
没有指针
,即那个变量还没被申明,那么就创建它
,指向undefined
。
在上面的案例中:
a.x
是没有指针
的,所以创建它
,指向undefined
a
是有指针
的,指向 {n:1}
- 后面找到的指针,都指向最右侧赋的那个值,即
{n:2}
所以执行以后,就有了如下的变量关系图
大家可以参考这张图,慢慢理解。
4、延伸
如果理解上面的解法,那么下面这几道题思路解法也差不多
var a = {n: 1}
var b = a
a = a.x = {n: 2}
console.log(a)
console.log(b)
var a = {n: 1}
var b = a
a = {n: 2}
a.x = a
console.log(a)
console.log(b)
本篇博客参考: