该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间: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); |
|
返回顶楼 | |
发表时间:2010-10-16
老师说了,连续赋值是危险的,尽量不要用。呵呵
应该还是赋值顺序的问题,这种从左到右形式,但在思考的时候容易先从右面开始。 |
|
返回顶楼 | |
发表时间: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的状况。 |
|
返回顶楼 | |
发表时间:2010-10-16
RednaxelaFX 写道 嘛,这也是运算符结合性(associativity)与求值顺序(order of evaluation)的概念分不清楚时容易弄错的问题。
结合性和求值顺序是没有必然关系的。 JavaScript的表达式的求值顺序都是从左向右的。赋值运算的结合性虽然是右结合,但同样是从左向右求值的。 这个解释不错。 |
|
返回顶楼 | |
发表时间:2010-10-16
最后修改:2010-10-16
这样写有意思吗???
以后谁维护这种代码? |
|
返回顶楼 | |
发表时间:2010-10-16
a.x = a = {n:2}
解释器首先执行的是a.x,不是赋值。找到a.x是在哪里,就不关心a是啥了。 所以相当于: ({n:1}).x = a = {n:2} 这么就容易理解了。 |
|
返回顶楼 | |
发表时间:2010-10-16
神奇!得记下来
|
|
返回顶楼 | |
发表时间:2010-10-16
最后修改:2010-10-16
trains629 写道 第二步时没有重新判断a的指向是否已经改变,直接按之前的指向处理的。 确实,就这一句最有用 |
|
返回顶楼 | |
发表时间: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的值不会改变了。。 |
|
返回顶楼 | |
发表时间: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的状况。 这个与结合性是没有关系的。别乱想了。 只与解释器的进栈地址有关。 |
|
返回顶楼 | |