输出以下代码的执行结果并解释为什么
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log (a.x);
console.log (b.x);
下面来分析下这段简单代码的工作步骤,从而进一步理解js引用类型“赋值”的工作方式。
首先第一行和第二行
var a = {n:1};
var b = a;
在这里a指向了一个对象{n:1}(以下称对象A),b指向了a所指向的对象,也就是说,在这时候a和b都是指向对象A的:
接着继续看下一行非常重要的代码:
a.x = a = {n: 2};
根据js引擎语法解析,会先去从左到右寻找有没有未声明的变量,如果有就把该变量提升至作用域顶部并声明该变量。那么恭喜js引擎他找到a.x这个属性没有声明,那么他会在{n: 1}这个内存区声明一个x属性等待赋值!如下图:
从图上可以看到,由于b跟a一样是指向对象A的,要表示A的x属性除了用a.x,自然也可以使用b.x来表示了。
语法解析完成后,开始进行赋值运算,首先依循“从右往左”的赋值运算顺序先执行 a={n:2} ,这时候,将a变量的指针指向了一个新的内存区{n: 2}(称为对象B),那么a变量脱离了对内存区{n: 1}的引用关系。
但是此时对象A这个内存区并没有被GC回收因为b变量的指针依然指向它。并且因为之前就声明了x属性所以该内存区增加了x属性。
接着继续执行 a.x=a,
此时a.x是保持对对象A中的x引用(即A.x),也就是b.x,由于赋值运算从右向左运算,所以a.x 在这个式子中 最终被指向了 {n:2} ,也就是对象B即A.x指向 对象B
所以当console.log(a.x)的时候,a是指向对象B的,但对象B没有属性x。就输出了 undefined;
b.x表示对象A的x属性,该属性是指向对象B,即{n:2}。