a.x = a = {n:2}

本文深入探讨JavaScript中对象赋值与引用的微妙之处,通过具体代码示例,详细解析了连等操作对对象属性及引用的影响,揭示了看似简单的赋值语句背后的复杂逻辑。

本文遵守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标签是否移动无关),当然,这种逻辑可能只有在连等的时候才会出现。

转载于:https://www.cnblogs.com/ShuolBDe/p/4489607.html

JavaScript 中,对象是通过引用进行赋值的。当一个对象被赋值给另一个变量时,该变量并不存储对象的实际值,而是保存对该对象在内存中位置的引用。这意味着多个变量可以指向同一个对象,并且对该对象的任何修改都会反映在所有引用上[^2]。 ### 对象赋值与引用机制 在以下代码中: ```javascript var a = { n: 1 }; var b = a; ``` 变量 `a` 和 `b` 都指向了同一个对象 `{n: 1}`。因此,如果通过 `a` 或 `b` 来修改这个对象,这种改变对另一个变量也是可见的。例如,若执行 `a.n = 3`,则 `b.n` 也会显示为 `3`。 ### 表达式 `a.x = a = {n: 2}` 的行为解析 该表达式涉及 JavaScript 的运算符优先级规则以及赋值操作的方向性。`.` 运算符(用于访问对象属性)具有比赋值运算符 `=` 更高的优先级。因此,在执行 `a.x = a = {n: 2}` 时,首先会尝试访问 `a.x`,此时虽然 `a` 尚未被赋予新值,但会先给当前 `a` 所指向的对象添加一个 `x` 属性,其值初始化为 `undefined`[^3]。 接下来,按照从右到左的顺序执行赋值操作: - `a = {n: 2}` 创建一个新的对象 `{n: 2}` 并将 `a` 指向它。 - 然后将这个新对象赋给之前通过 `a.x` 访问并标记为 `undefined` 的属性。此时,尽管 `a` 已经指向新的对象,但 `a.x` 的访问发生在 `a` 被重新赋值之前,所以它实际上是在原始对象上设置 `x` 属性为新对象 `{n: 2}`。 最终结果是 `a` 指向了新的对象 `{n: 2}`,而原来的对象 `{n: 1}` 现在有一个 `x` 属性,其值为新的对象 `{n: 2}`。同时,由于 `b` 仍然指向最初的对象,即 `{n: 1, x: {n: 2}}`,所以 `b.x` 的值就是 `{n: 2}`。 ### 控制台输出分析 ```javascript console.log(a.x); // 输出 undefined console.log(b.x); // 输出 {n: 2} ``` - `a.x` 输出 `undefined` 是因为 `a` 现在指向新创建的对象 `{n: 2}`,而此对象并没有 `x` 属性。 - `b.x` 输出 `{n: 2}` 是因为 `b` 依然指向最初的对象 `{n: 1}`,并且该对象的 `x` 属性已经被更新为新对象 `{n: 2}`。 这种行为展示了 JavaScript 中对象引用和赋值机制如何影响程序的状态和数据结构的变化。 ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值