a.x = a = {n:2}

本文遵守CC BY-SA 3.0

前言:

  标题真是不知道如何命名,就取最重要的一句话吧。话说最近在看javascript权威指南,感觉对这个语言有种莫名的喜欢。。。这个也应该是一个比较经典的问题了,不是出自此书,据说是jQuery源码中的一个用法,网上找了一阵,stackoverflow上直接是0 results。。可能是问题比较久远,貌似都是10年左右的回答,而且也没有什么让我豁然开朗的解答,倒是在回复中看到了一个思路,于是就顺着想下来了,如果有什么错误,还请斧正。

  先把问题放在这里  

1 var a = {n:1}
2 var b = a //暂存a
3 a.x = a = {n:2} //问题的源头
4 console.log(a.x)
5 console.log(b.x)

  想想看,输出结果都为什么值。

一、梳理

行号1:创建"{n:1}"对象,并将a指向此地址空间(假设为A);

行号2:将b指向a指向的地址空间(还是A);

行号3:

  (1) 在赋值没开始的时候:a.x是给地址A中存储的对象分配了一个新属性x,并分配了地址(假设AX);

  (2) 开始赋值,由于赋值是右结合运算,所以这句话可以看作a.x = (a = {n:2});

  再次分布解析,注意,此时a.x的地址空间是AX,a的地址空间是A,{n:2}被作为新对象被创建,并且分配地址空间(假设B)

  1. 其实之所以混乱就是被变量名迷惑了,只要将变量都看作对地址空间或其内部值的操作就简单明了了,比如:

    a.x = (a = {n:2})可以看作:

    a指向的地址空间被更改成对象{n:2}的地址空间;

    a.x中的a的地址空间,即A地址空间存储的x属性(AX地址中的值)被赋值成{n:2};

  2. 分析结果

    整体赋值完成后,a指向的地址空间更新成C,内部值为{n:2};

    还记得b指向的地址空间吗,就是最初的a指向的A,虽然连续赋值语句没有对b进行操作,但是A这块地址空间却被修改了(被赋予新属性x,值为{n:2})

行号4: 输出 a.x 为undefined。

行号5: 输出 b.x 为{n:2}。

二、再努力一次

  如果看完梳理仍然觉得懵逼,可以尝试这么想:

  可能大家对a = {n:2}这句话没什么问题,实际上,所有皆可看作对象,所有皆可作为引用,于是在连等的语句中,对a.x赋值的时候可以看作是在对A地址空间的x属性进行复制,这个连等操作也实际上是在对各个地址空间的值或属性进行修改,所以,即使我们看到的a被修改了,它最初代表的地址空间没有改变,可以参照下面的例子

  现在有三个杯子(A/B/AX),两个标签(a/b/c),一个标记(x1,x2)

  1. 向杯子A中放入一号小球(n:1),然后将a/b标签都贴在杯子A上,c标签贴在杯子AX上;

  2. 向杯子B中放入二号小球(n:2),

  3. 向A中投入一个x1标记,告诉你可以到杯子AX中察看(a.x的创建),AX中投入一个x2标记,告诉你可以到B中找到二号小球(对a.x赋值);

  4. 将a标签贴到B杯子上面。

  5. 这个时候,看看各个物件的状态,

    (1)a标签贴在了B上,b标签还贴在A上,c标签还贴在AX上;

    (2)A里面有个1号小球(n:1),一个指向AX的x1标记;

    (3)AX里面有个x2标记,指向B;

    (4)B里面有个2号小球(n:2);

  6. 察看结果:

    a.x => B杯子中的x标记(没有,所以undefined)

    b.x => A杯子中的x标记(指向杯子AX,再指向杯子B,发现是个2号小球(n:2));

  注:可能看到这里你还是有疑问,为什么a标签的转移要放到最后一步,明明是应该在中间步骤啊,其实在处理赋值语句的时候,如果你修改了属性(x),那将对该引用指向的地址空间存储的值进行修改,也就是改值而非改引用(杯子没变),而对a的赋值才是对引用的修改(移动a标签),也就是说,做a的赋值的时候代表着移动a标签,而对a.x的赋值则代表着对A杯子的x标记作处理,可以理解成,做赋值之前,a.x就已经代表了对A杯子的操作(与期间a标签是否移动无关),当然,这种逻辑可能只有在连等的时候才会出现。

posted @ 2015-05-09 11:44  ShuolBDe  阅读(386)  评论(0编辑  收藏  举报