问题
关于这段代码,如果有被迷惑过的同学,可能会很熟悉
let i = 0;
i = i++;
console.log(i);
结果输出为:0
关于 i=i++ 的问题,在百度上一搜一大堆,很多文章会直接通过汇编来看内存中是如何操作,还有一些是说这个问题,其实在早年是没有直接定义的,有些非常的老的编译器,可能会输出1,但是现在的编译器几乎不可能输出1,都是一样的结果为 0。这个问题早就不存在歧义了,只是在开发中几乎不会用到这种表达式。
解读
第二行代码 i = i++;
:
这行代码执行时,总共有二个步骤,先运算再赋值。运算这个步骤(i++
)是一个后置递增运算符,那我们就先从这个后置递增运算符说起。
运算 i++
:
- 首先 i 的原始数据复制一份放在一块内存中,我把这块内存叫做副本,i 的原始数据是0,那么副本的值就是0,然后原始数据开始递增0 + 1,那么就是1,注意这里,此时已经有两块内存存在了,并不是一块内存,原始数据占一块内存,而副本占一块内存,也就是说,原始数据那块内存的递增并不影响副本那块内存的值,好了,明白了这一点就好办了。
赋值 i = ?
- 到赋值这个步骤了,此时的 i 已经是1了,但是哪个值赋给 i 呢?是副本还是原始数据?是副本,所以
i = 0
。最终结果 i 是0而非1。
我们现在明白了这个后置递增运算符的执行步骤了,那我们来算一个复杂一点的后置递增运算语句:
let i = 10;
i = i++ + i++;
console.log(i);
我们直接解读这一部分(i = i++ + i++
)的执行步骤:
首先我们拆分大体的执行步骤:
i++
+
i++
i = ?
-
这个后置递增运算符执行完,就会另外增加一块内存(副本),那么就有两块内存,一块是副本10,一块是原数据11。从上面的讲解得出,被当做操作数的是副本而不是原始数据,所以10被当做操作数。
-
10 + 多少呢?我们接下来看下一步。
-
此时的 i 是多少了,从第一步可以得出,i 已经是11了,然后老规矩,复制–>递增之后副本是11,原始数据是12,副本被当做操作数,那么就是 10 + 11 = 21
-
i = ?
,又到赋值部分了,21赋值给 i ,i 的最终结果是21
最后我们再来看一段代码:
let i = 10;
console.log(i++);
当第二行码执行完,i 已经是11,但是被当做操作数输出的不是原始数据而是副本,所以输出10。
注意点
i++
后置递增运算符是一个整体,自然它的执行步骤也不能被拆开(复制–>递增),这个两个步骤是紧紧连在一起的。
我们来看一个将后置递增运算符的执行步骤拆开之后的运行结果:
// 还是老演员哈
let i = 10;
i = i++ + i++;
console.log(i);
i = i++ + i++;
:
-
i++
:复制一块副本10,原始数据10 -
+
:10 + ? -
i++
:先复制,原始数据是10,所以副本也是10,10 +10 = 20 ,再递增,那么按道理来说应该递增两次,第一步还没有递增呢,递增两次,那么 i 原始数据为12。 -
i = ?
:i = 20,最终结果是20 。这个结果对吗?不对。
我们再来复盘一下它的执行顺序,复制–>复制–>加法运算–>递增–>递增,两个后置递增运算符的执行步骤都被拆开了,导致得到错误的结果。正确的执行步骤应该是这样(复制–>递增)+ (复制–>递增),因为i++
是一个整体,所以它的执行步骤用括号括起来便于理解。
总结
后置递增或者递减运算符,是先复制一块副本再递增或者递减原数据,副本被当做操作数而非原数据。
前置递增或者递减运算符,是先递增或者递减原数据再复制一块副本,副本被当做操作数而非原数据。它的副本和原数据的值都相同,所以前置这一块不会有什么疑惑的地方。
要把i++
++i
或者i--
--i
看成一个整体,自然它们的执行步骤也不能被拆开。
ppt动画
如果你觉的看文字难以理解,我还做了一个ppt
测试代码
这个文件是一些更加复杂的前置后置递增递减运算符代码,感兴趣的话可以来测试一下,看一下你答对了吗?
资料在百度网盘中,如果要提取码,那么就是:1111