一段不算太新,流传网络的代码:
var a = (++Math.PI); alert(a); //1 alert(Math.PI); //2 var b = (Math.PI++); alert(b); //3 alert(Math.PI); //4 var c = Math.PI = (++Math.PI); alert(c); //5 alert(Math.PI); //6 var d = Math.PI = (Math.PI++); alert(d); //7 alert(Math.PI); //8 var e = Math.PI = (Math.PI + 1); alert(e); //9 alert(Math.PI); //10
Math.PI 在其他语言看来是系统一个常量,修改它的话编译都通不过,不过 javascript 具有很强的的容错性(包容性?),他是不会提醒你的,要搞懂这个不看规范是不行的。
分析
ECMAScript 262 :
PI
15.8.1.6 PI
The number value for π, the ratio of the circumference of a circle to its diameter, which is approximately 3.1415926535897932.
This property has the attributes { DontEnum, DontDelete, ReadOnly }.
只有系统属性才会有 readonly ,我们定义的一般属性没法设置, 而 ReadOnly 应该相当于常量了吧 ?
ReadOnly
8.6.1 ReadOnly
The property is a read-only property. Attempts by ECMAScript code to write to the property will be ignored. (Note, however, that in some cases the value of a property with the ReadOnly attribute may change over time because of actions taken by the host environment; therefore “ReadOnly” does not mean “constant and unchanging”!)
看见一般情况下,readonly属性不可以改,但不报错,即赋值修改没用!也不意味着是常量 !
那么现在知道了 : 2,4,6,8,10 条代码 alert 都是 3.14 了。
另外 3 alert 也不用解释了,返回自增前结果,还是 3.14
那么 1 alert 难道还是 3.14 ,因为 PI readonly 无法改变 ?错误
自增运算符
涉及到 前缀自增运算符的返回值问题 :
11.4.4 Prefix Increment Operator The production UnaryExpression :
++ UnaryExpression is evaluated as follows:
1. Evaluate UnaryExpression.
2. Call GetV alue(Result(1)).
3. Call ToNumber(Result(2)).
4. Add the value 1 to Result(3), using the same rules as for the + operator (see 11.6.3).
5. Call PutV alue(Result(1), Result(4)).
6. Return Result(4).
则可见返回的是 Result(4) Math.PI+1 ,而不是 第五步过后的 Result (1)
那么可见 1 alert 即为 Math.PI+1 = 4.14
赋值操作符
c,d,e 类似,= 操作符具有右结合性, 则申明初始化时,变量值为赋值表达式 (Math.PI = ++Math.PI) ,javascript 每个表达式都会返回一个值,这就涉及到了前句的表达式返回值是多少:
11.13.1 Simple Assignment ( = )
The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:
1. Evaluate LeftHandSideExpression.
2. Evaluate AssignmentExpression.
3. Call GetV alue(Result(2)).
4. Call PutV alue(Result(1), Result(3)).
5. Return Result(3).
前面自增规范可以得到 ++Math.PI 返回 4.14 为 Result 2,Result 3 ,同样的道理,第4步赋值后,返回的是赋值表达式右端的值 (++Math.PI),而不是左端的值 Math.PI ,则 (Math.PI = ++ Math.PI) 返回 ++Math.PI == 4.14 ,即连续赋值运算只返回最右边的值,而不管赋值中变量是否发生了变化。