论坛首页 Web前端技术论坛

写了10年Javascript未必全了解的连续赋值运算

浏览 32193 次
该帖已经被评为良好帖
作者 正文
   发表时间:2010-10-16   最后修改:2010-10-16
LZ又是猜想、又是证明、又是解惑的,但绕了一大圈也并没有把问题说清楚。

其实,最合理的解释也就是下面的,一句话就足够了。

暴走的酱油瓶 写道
这个现象还真出乎意料之外,不看结果的话肯定以为是猜想2,但是a.x会被赋值。感觉像是js引擎为了效率,在执行
a.x = a = {n:2};
1、a = {n:2};
2、a.x = {n:2};
第二步时没有重新判断a的指向是否已经改变,直接按之前的指向处理的。


再引用Clue的,说明这种赋值方式不只是JavaScript、Java也是如此:
O o = new O("1");  
O t = o;  
o.a = o = new O("2");  
System.out.println(o.id + o.a + t.a); 


0 请登录后投票
   发表时间:2010-10-16  
老师说了,连续赋值是危险的,尽量不要用。呵呵

应该还是赋值顺序的问题,这种从左到右形式,但在思考的时候容易先从右面开始。
0 请登录后投票
   发表时间:2010-10-16  
嘛,这也是运算符结合性(associativity)与求值顺序(order of evaluation)的概念分不清楚时容易弄错的问题。
结合性和求值顺序是没有必然关系的。

JavaScript的表达式的求值顺序都是从左向右的。赋值运算的结合性虽然是右结合,但同样是从左向右求值的。看ECMAScript 5的附录D,
11.8.2, 11.8.3, 11.8.5: ECMAScript generally uses a left to right evaluation order, however the Edition 3 specification language for the > and <= operators resulted in a partial right to left order. The specification has been corrected for these operators such that it now specifies a full left to right evaluation order. However, this change of order is potentially observable if side-effects occur during the evaluation process.
这里的描述说明ECMAScript是使用从左向右的求值顺序

AssignmentExpression :
    ConditionalExpression
    LeftHandSideExpression AssignmentOperator AssignmentExpression

这条语法规则定义了赋值运算符是右结合的。怎么看出来呢?
首先要能读懂ECMAScript规范里语法的记法。在冒号左边的是语法规则的名字,右边的是规则的推导内容。推导内容中,在同一行上的属于同一条子规则,在不同行上的属于不同子规则;不同子规则之间是“或”的关系。
上面的语法规则的意思是:
一个“赋值表达式”,
  可以由一个“条件表达式”构成;
  或者可以由一个“左手边表达式”加上一个“赋值运算符”加上一个“赋值表达式”构成。

如果在一条语法规则里,推导内容中出现了该规则自身,则这条规则是“直接递归”的。如果自身出现的位置在推导内容某条子规则的最左边,则为“左递归”,出现在最右边则为“右递归”。运算符结合性也正好在此体现:左递归的规则意味着左结合,右递归的规则意味着右结合。
可以看到,ECMAScript的赋值表达式的语法是右递归的,因而是右结合的。

这两者对程序执行有什么影响呢?前面clue的解答已经对路了。有兴趣看结合性、优先级和求值顺序的关系的例子的请参考我之前的一帖,虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩,中间讲到抽象语法树的时候有举例。Java与C#同JavaScript一样,是用从左向右的求值顺序,而赋值运算是右结合的,所以那帖的例子也可以用来帮助理解JavaScript的状况。
2 请登录后投票
   发表时间:2010-10-16  
RednaxelaFX 写道
嘛,这也是运算符结合性(associativity)与求值顺序(order of evaluation)的概念分不清楚时容易弄错的问题。
结合性和求值顺序是没有必然关系的。

JavaScript的表达式的求值顺序都是从左向右的。赋值运算的结合性虽然是右结合,但同样是从左向右求值的。


这个解释不错。
0 请登录后投票
   发表时间:2010-10-16   最后修改:2010-10-16
这样写有意思吗???
以后谁维护这种代码?
0 请登录后投票
   发表时间:2010-10-16  
a.x = a = {n:2}
解释器首先执行的是a.x,不是赋值。找到a.x是在哪里,就不关心a是啥了。
所以相当于:
({n:1}).x = a = {n:2}
这么就容易理解了。
0 请登录后投票
   发表时间:2010-10-16  
神奇!得记下来
0 请登录后投票
   发表时间:2010-10-16   最后修改:2010-10-16
trains629 写道

第二步时没有重新判断a的指向是否已经改变,直接按之前的指向处理的。

确实,就这一句最有用
0 请登录后投票
   发表时间:2010-10-16  
楼主说的很绕,其实就是一个值引用的问题

var a={}
var b=a;


其实a、b都是指向{}的一个指针,如果a或者b修改,{}这个对象也会修改。。比如a.x=3,那么{}这个对象就修改了。。因为b也指这个对象,所以b也修改了。。。

但是  a.x=a=3

表达式从坐到右,a.x=3首先修改了对象,对象改了,b当然也变化了;但是a=3,a指向了其他的地方,但是并没有修改对象,所以b的值不会改变了。。
0 请登录后投票
   发表时间:2010-10-17  
嘛,这也是运算符结合性(associativity)与求值顺序(order of evaluation)的概念分不清楚时容易弄错的问题。
结合性和求值顺序是没有必然关系的。

JavaScript的表达式的求值顺序都是从左向右的。赋值运算的结合性虽然是右结合,但同样是从左向右求值的。看ECMAScript 5的附录D,
ECMAScript 5 写道
11.8.2, 11.8.3, 11.8.5: ECMAScript generally uses a left to right evaluation order, however the Edition 3 specification language for the > and <= operators resulted in a partial right to left order. The specification has been corrected for these operators such that it now specifies a full left to right evaluation order. However, this change of order is potentially observable if side-effects occur during the evaluation process.
这里的描述说明ECMAScript是使用从左向右的求值顺序

AssignmentExpression :
    ConditionalExpression
    LeftHandSideExpression AssignmentOperator AssignmentExpression

这条语法规则定义了赋值运算符是右结合的。怎么看出来呢?
首先要能读懂ECMAScript规范里语法的记法。在冒号左边的是语法规则的名字,右边的是规则的推导内容。推导内容中,在同一行上的属于同一条子规则,在不同行上的属于不同子规则;不同子规则之间是“或”的关系。
上面的语法规则的意思是:
一个“赋值表达式”,
  可以由一个“条件表达式”构成;
  或者可以由一个“左手边表达式”加上一个“赋值运算符”加上一个“赋值表达式”构成。

如果在一条语法规则里,推导内容中出现了该规则自身,则这条规则是“直接递归”的。如果自身出现的位置在推导内容某条子规则的最左边,则为“左递归”,出现在最右边则为“右递归”。运算符结合性也正好在此体现:左递归的规则意味着左结合,右递归的规则意味着右结合。
可以看到,ECMAScript的赋值表达式的语法是右递归的,因而是右结合的。

这两者对程序执行有什么影响呢?前面clue的解答已经对路了。有兴趣看结合性、优先级和求值顺序的关系的例子的请参考我之前的一帖,虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩,中间讲到抽象语法树的时候有举例。Java与C#同JavaScript一样,是用从左向右的求值顺序,而赋值运算是右结合的,所以那帖的例子也可以用来帮助理解JavaScript的状况。


这个与结合性是没有关系的。别乱想了。
只与解释器的进栈地址有关。
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics