引子
一道阿里的面试题
let a = {
n:2
}
let b = a
a.x = a = {
l:2
}
// 说出 a 和 b的输出值
我第一次看到这个题目是懵逼的,因为平时没注意=
的多次赋值的执行顺序的问题。
第一种答案:
a => {
l:2,
x:{
l:2
}
}
b=> {
n : 2
}
第二种答案:
a => {
l:2
}
b=> {
n : 2,
x:{
l:2
}
}
答案:第二种
分析
=
是不论前面有多长,最终的取值都取决于最末的赋值,有点不太好理解,我们做几个编译试试就明白了。
就拿上面的例子看看,我们调换赋值的顺序得到的结果是一样
let a = {
n:2
}
let b = a
a = a.x = {
l:2
}
输出的值为:
a => {
l:2
}
b=> {
n : 2,
x:{
l:2
}
}
我们修改下a
的值来看下,b.x
是否与a
是否应用同一个对象
let a = {
n:2
}
let b = a
a.x = a = {
l:2
}
a.l = 3
console.log(a,b)
输出:
a => {
l:3
}
b=> {
n : 2,
x:{
l:3
}
}
很明显引用的对象是同一个,并不是声明了2个对象进行赋值
结论
- 多次赋值与顺序无关,是同时进行赋值的
- 每个节点的变量最终赋值的值取决去最后一个等号的右边值
- 如果赋值是引用类型,则最终指向的是同一个对象
思考
其实我们直接使用变量名字的时候,其实使用的是这个变量名的地址。
当执行到连续赋值a.x=a={n:1}
这个语句的时候,这里的代码环境会进行解析,看起来只是单纯地涉及了一个变量a
在环境中,已经存在了一个a
,我们知道第一次使用var
会进行声明提升,而如果没有使用过var
的话,这个变量就会变成全局变量。
我们回到原题上,.
这个是进行对象访问,你也可以通过a["x"]
来进行访问,因为我们可以直接使用.
来进行直接赋值,所以这个.
自身带有赋值声明
的功能,如何体现呢?
我们试想下,如果直接返回一个没有申明过的变量会怎么样呢?
会直接报错,不可以引用未声明过的变量。
而仅仅是声明变量没有赋值,我们进行输出会返回undefined
如果我们通过使用.
来引用一个没有声明过的属性,它却会返回undefinded
这个属性,这样非常明显地说明了通过.
是会进行声明变量的功能
这就可以解释为什么a.x
和a
这2个a
为什么会不相等
因为a.x
引用的是堆内存的里面的属性变量,在解析的时会直接对这个地址的变量进行操作,而a
时访问变量栈内存里面这个变量名,所以进行赋值操作并不会影响前者,因为解析这个语句时一次性全部读取再进行赋值,而不是进行分段读取赋值的。
·所以就解释了与顺序无关,所有的属性都将会被赋值到最右的值·
这里涉及了作用域链、VO/AO等一些原理,希望大家如果想要深入学习前端的话,建议学习下这些概念,对分析JS执行非常有帮助。