Closed kuitos closed 9 years ago
几个月前,不知道什么缘由跟松波同学讨论了起js里自增操作符(i++)的问题,现将前因后果整理出来,传于世人😂
事情起源于这样一段代码
var i = 0; i = i++; console.log(i);
来,都来说说答案是啥? 结果是0 换一种形式,或许大家不会有多少疑问
0
var i = 0; var a = i++; console.log(a); // 0
没错,这也是我们初学自增操作符的经典例子,对这结果还有疑问请自觉面壁。。。 遥想当年学习自增操作符的口诀大致是,i++ 是先用后自增,++i 是先自增再用 那么按照这个思路,上面的代码解析流程应该是这样的
i++ 是先用后自增,++i 是先自增再用
var i =0; i = i; i = i + 1;
可惜结果并不是这样的 按照犀牛书上的描述,后增量(post increment)操作符的特点是
它对操作数进行增量计算,但返回未作增量计算的(unincremented)值。
但是书上并没有告诉我们,先做增量计算再返回之前的值,还是返回之前的值再做增量计算。 对于这种疑问,我们只能求助ecmascript给出官方解释:
The production PostfixExpression : LeftHandSideExpression [no LineTerminator here] ++ is evaluated as follows: Evaluate LeftHandSideExpression. Call GetValue(Result(1)). Call ToNumber(Result(2)). Add the value 1 to Result(3), using the same rules as for the + operator (see 11.6.3). Call PutValue(Result(1), Result(4)). Return Result(3).
The production PostfixExpression : LeftHandSideExpression [no LineTerminator here] ++ is evaluated as follows:
从es上的算法描述,我们能够清晰的得知,后自增操作符是先自增赋值,然后返回自增前的值,这样的一个顺序。 到这里还不算完。 既然i=i++这种操作最后i还是为原始值,也就是这段代码不会有任何实际意义,那么js引擎有没有可能针对性的做优化,从而避免不必要的自增运算?(如果你用的是IDE,IDE会提示你这是一段无用的代码) 也就是说,我们如何确定,执行引擎一定做了两步操作:
i=i++
i = i + 1;
i = iBeforeIncrease;
还是执行引擎可能会针对性的优化,只做一步操作:
当我在想怎么去确定这一点时,松波给出了解决方案,用Object.observe()方法啊!!(该方法是ES7提案中的新api,不过chrome早早的实现了)
var obj = {i:0}; Object.observe(obj, function(changes){ console.log(changes); }); obj.i = obj.i++;
代码放到chrome中跑一下,可以看到,改变触发了两次,也就是i做了两次修改操作。 另外firefox中也提供了一个类似的api,Object.prototype.watch,有兴趣的同学可以试试用这个方式来验证一下。
顺便抖个机灵,自增操作是非原子性操作,是非线程安全的,多线程环境下共用变量使用自增操作符是会有问题的(前端同学们别急,ES7会为我们带来js多线程编程体验😍)。
ES规范解读之自增操作符
几个月前,不知道什么缘由跟松波同学讨论了起js里自增操作符(i++)的问题,现将前因后果整理出来,传于世人😂
事情起源于这样一段代码
来,都来说说答案是啥?
结果是
0
换一种形式,或许大家不会有多少疑问
没错,这也是我们初学自增操作符的经典例子,对这结果还有疑问请自觉面壁。。。
遥想当年学习自增操作符的口诀大致是,
i++ 是先用后自增,++i 是先自增再用
那么按照这个思路,上面的代码解析流程应该是这样的可惜结果并不是这样的
按照犀牛书上的描述,后增量(post increment)操作符的特点是
但是书上并没有告诉我们,先做增量计算再返回之前的值,还是返回之前的值再做增量计算。 对于这种疑问,我们只能求助ecmascript给出官方解释:
Postfix Increment Operator(后自增操作符)
从es上的算法描述,我们能够清晰的得知,后自增操作符是先自增赋值,然后返回自增前的值,这样的一个顺序。
到这里还不算完。
既然
i=i++
这种操作最后i还是为原始值,也就是这段代码不会有任何实际意义,那么js引擎有没有可能针对性的做优化,从而避免不必要的自增运算?(如果你用的是IDE,IDE会提示你这是一段无用的代码)也就是说,我们如何确定,执行引擎一定做了两步操作:
i = i + 1;
return iBeforeIncrease = 0;i = iBeforeIncrease;
还是执行引擎可能会针对性的优化,只做一步操作:
i = iBeforeIncrease;
当我在想怎么去确定这一点时,松波给出了解决方案,用Object.observe()方法啊!!(该方法是ES7提案中的新api,不过chrome早早的实现了)
代码放到chrome中跑一下,可以看到,改变触发了两次,也就是i做了两次修改操作。
另外firefox中也提供了一个类似的api,Object.prototype.watch,有兴趣的同学可以试试用这个方式来验证一下。
顺便抖个机灵,自增操作是非原子性操作,是非线程安全的,多线程环境下共用变量使用自增操作符是会有问题的(前端同学们别急,ES7会为我们带来js多线程编程体验😍)。