javascriptvar a = {n:1}; var b = a; // 持有a,以回查 a.x = a = {n:2}; alert(a.x);// --> undefined alert(b.x);// --> {n:2}
请问结果为何是这样?
我的理解是连等赋值从右向左运算的,当a被复制为{n:2}之后,
为什么a.x中的a仍然指向{n:1}?
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
同意3楼和4楼同学说的。连等是先确定所有变量的指针,再让指针指向那个赋值(
{n:3})。对于
a.x = a = {n:2},楼主原先的思路应该是:{n:2}赋值给 a{n:2}再赋值给 a.x这样似乎确实说不通 a.x 的值是 undefined,因为 a.x 确实是被赋值了的啊。
可是事实上,a.x 的值却是 undefined。
再来看一下这个:
a = a.x = {n:2}的话,按楼主原先的思路应该是:{n:2}赋值给 a.x,那么也就相当于b.x = {n:2}啦{n:2}。那么这是后 a.x 的值确实是 undefined,a 对象{n:2}中就没有 x 属性嘛。按楼主的思路,上述两种方式的结果应该是不同的。但事实却是
a = a.x = {n:2}和a.x = a = {n:2}的结果是一致的。所以楼主的那种赋值的思路是不对的。事实上,解析器在接受到
a = a.x = {n:2}这样的语句后,会这样做:a 是有指针的,指向
{n:1};a.x 是没有指针的,所以创建它,指向 null。{n:2}。所以执行以后,就有了如下的变量关系图。楼主可以慢慢体会下,想通了就很简单的。
赋值是从右到左的,但不要被绕晕了, 其实很简单,从
运算符优先级来考虑.运算优先于=赋值运算,因此此处赋值可理解为赋值结果:
答案写在纸上了:希望能看懂
另外附上一个链接,看完也有助理解:javascript里面不同function定义的区别,我觉得前面的那个链接也不错。
这个问题如果对js或者说对计算机语言不熟悉很容易陷在里面。
有些问题并不难,想的简单些也更容易理解些,复杂了未必是好事,思考的方式很重要
理解这个其他的都好理解了,这个赋值是从右边向左边进行的。但是在真正赋值的时候,并不是看上去的那样。真正的赋值表面上和下面的一样:
但因为a只是一个变量,在内部的时候其实是这样的:
然后运行的时候,a地址指向的值增加了一个x属性,由于b引用这个值,因此b可以访问这个x的值。而a这个变量就被赋值给了{n:2}了。
要知道js在运行前有语法错误检查和一些其他的准备的,不是像我们写的代码看上去那样。也就是说,如果涉及到变量的属性的赋值什么的,都会对原变量保存一个副本,在这个副本上进行操作,来保证属性的确是被附到了原来的对象上。这个我好像在哪个书上看到过,但是记得有些模糊了,可能有错误的地方。
虽然这个问题过去很长时间了,但是今天我来个让所有人一看就懂的回答:
但是,你真正懂得右结合性是怎样起作用的吗?
看下面的连续赋值表达式:
其中的exp是一个表达式,并且除最后一个expN外,其他表达式都必须可以作为左值。
你能告诉我它是怎样运算吗?
是这样的,首先根据赋值运算的右结合性,可以改写成:
然后按照下面步骤进行运算:
解析exp1;
解析exp2;
解析exp3;
...
N. 解析expN;
以上步骤完成后,上面表达式变成了:
其中
value是表达式expN的值。接下来的步骤是:将value赋给引用refN-1;
将value赋给引用refN-2;
...
N-1.将value赋给引用ref1;
结束。
现在你应该可以理解为什么
是怎么回事了。关键在这里:
a.x是对a这个对象下的x这个名称的引用。a可以看成envRec.a,是对envRec这个对象下的a这个名称的引用。envRec是什么呢?是当前执行上下文中的环境记录项,可以认为保存局部变量的那个内部对象。我理解这涉及两个过程即求值过程和运算过程,求值过程都是从左到右,而运算过程有自己的运算顺序
这条语句中a.x a 和 {n:2}分别是三个表达式,需要先对他们求值,然后才会进行赋值操作,求值过程是先于赋值过程的
@justjavac
写了 10 年 Javascript 未必全了解的连续赋值运算
问题在于连等语句,是一条语句啊!
在程序运行到之后,先确定好了
a.x和a的引用,再从右往左开始赋值的。. 运算优先于 = 赋值运算,且赋值从右到左
因此 a.x = a = {n:2} 可以分解为
a.x = {n:2}; // b 作为引用,即 b = {n:1, x:{n:2}}
a = {n:2}; // a 被重新赋值对象,即 b 不再为引用