【转载】连续赋值和内存指针的问题解析(a.x=a={n:2})
前几天偶然看到了一个这样的题:
var a = {n:1};
var b = a;
a.x = a = {n:2};//关键代码
console.log(a);//{n: 2}
console.log(b);//{n: 1, x: {n: 2}}
这个题代码不多,不过考察的知识点却非常不错。我们知道内存空间分为栈内存和堆内存。
栈内存用来存放供js代码来执行的环境,所以为了保证性能减少内存占用,我们一般把占用空间较小的类似于基本数据类型放在栈内存中,像引用数据类型这种占据空间比较大的东西,我们需要将它放在一个贮藏室中—堆内存中,堆内存相较于栈内存对js执行时对性能影响很小,虽然引用类型存放在堆内存里了,但是当我们代码执行时我们也需要用到这部分,所以我们需要将类似于标签,名字(类似于元素的ID)放置于栈内存中,和堆内存中的东西一一对应,当执行到他时直接去堆内存中找,这种模式就好比古代的银票和同等价值的黄金和白银,一个很有钱的商人不能每次出门都推着一车银子吧,那可是金属,那这样有钱人活的就太累了,所以一个等面值的银票揣在手中,一切问题就搞定了。 所以“不同变量的同一引用数据类型赋值就是指向的是同一个堆内存空间,不会去创造新的内存空间,不然太浪费空间了,你要知道这可是内存,内存呀!!”
1. 像此题中一样 var a={n:1}; var b=a; 这是并没有给变量b在堆内存中在创建一个对象{n:1},而是他俩共同指向内存空间中的{n:1},不论你创建多少个变量去赋值堆内存中始只存在一个{n:1};我们姑且称呼这个对象为A,草图如下:
2. 接下来我们可以看到此题中有标注‘关键代码’字样的,他们考察了我们连续赋值,并且还有属性操作:
a.x = a = {n:2};
js的赋值运算顺序永远都是从右往左的,不过由于“.”是优先级最高的运算符,所以这行代码先“计算”了a.x。
(注意,这里说的a.x真的只是初始化一下a.x ;而不是“计算”a.x = a)
所以我们先进行属性操作,a.x,因为此时x作为a的一个属性,没有最开始没有从右向左赋值时它的值为undefined,这行代码可以说已经执行了a.x,这时如下图
接下来从右向左开始赋值,当a的整个对象更改了,堆内存中就会新建一个对象,然后a的地址也会更改,不过b的地址并没有更改还是会指向原来的对象,如下图,将a的指向从对象A指向了对象B{n:2}
接下来继续执行 a.x = a,很多人会认为这里是“对象B也新增了一个属性x,并指向对象B自己”,很多人这样认为是错误的,根据优先性( . 运算符最先计算),咱们刚才已经计算过a. x,那时变量a的指向还是对象A,变量a指针(地址)并没有发生变化,所以此时a.x确切地说应该是对象A.x,代码再次执行时,指针发生了变化,a的指针指向B,此时a.x = a再赋值时不再次执行依次a.x然后再赋值,这样系统不允许,而是直接赋值,相当于之前的A.x赋值,所以 a.x=a 应理解为对象A的属性x指向了对象B:
那么根据图表很容易得出结果当console.log(a.x)的时候,a是指向对象B的,但对象B没有属性x。没关系,当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。但当查找到达原型链的顶部 - 也就是 Object.prototype - 仍然没有找到指定的属性B.prototype.x,自然也就输出undefined;
为了验证咱们的结果我们可以将上面的题进行变种:
var a = {n:1};
var b = a;
a = {n:2}
a.x = a;
console.log(a.x);//[object,Object]
console.log(b.x);//undefined
那么与上题的区别就是我们将变量a指针先改变,然后在进行对象属性的操作a.x。
嘿嘿,如果我们这样连等呢?
var a = {n:1};
var b = a;
a = a.x = {n:2};
console.log(a.x);
console.log(b.x);
console.log(a.n);
console.log(b.n);
此时的结果是和第一题的解题思路是一样的,只要是既有变量和变量属性的连等赋值的打印元素属性的值都为undefined
总结:所以我们做此类题时无外乎考虑这几个知识点连等赋值的优先级别从右向左,不过遇见属性一切玩完,
我验证了一下凡是类似于这样变量a=a.x=引用数据类型,都可以引用只要是既有变量和变量属性的连等赋值的打印元素属性的值都为undefined。
本篇文章内容为转载,原链接:https://www.cnblogs.com/peakol/p/7074289.html