建议21:推荐提高循环性能的策略(2)
在每个循环中,每次运行循环体都要发生如下操作:
第1步,在控制条件中读一次属性(items.length)。
第2步,在控制条件中执行一次比较(i < items.length)。
第3步,比较操作,观察条件控制体的运算结果是不是true(i < items.length == true)。
第4步,一次自加操作(i++)。
第5步,一次数组查找(items[i])。
第6步,一次函数调用(process(items[i]))。
在这些简单的循环中,即使没有太多的代码,每次迭代都要进行这6步操作。代码运行速度很大程度上由process()对每个项目的操作所决定,即便如此,减少每次迭代中操作的总数也可以大幅度提高循环的整体性能。
优化循环的第一步是减少对象成员和数组项查找的次数。在大多数浏览器上,这些操作比访问局部变量或直接量需要更长时间。例如,在上面代码中,每次循环都查找items.length,这是一种浪费,因为该值在循环体执行过程中不会改变,因此产生了不必要的性能损失。我们可以简单地将此值存入一个局部变量中,在控制条件中使用这个局部变量,从而提高了循环性能,例如:
- for (var i=0, len=items.length; i < len; i++){
- process(items[i]);
- }
- var j=0, count = items.length;
- while (j < count){
- process(items[j++]);
- }
- var k=0, num = items.length;
- do {
- process(items[k++]);
- } while (k < num);
这些重写后的循环只在循环执行之前对数组长度进行一次属性查询,使控制条件中只有局部变量参与运算,因此速度更快。根据数组的长度,在大多数浏览器上总循环时间可以节省大约25%,在IE浏览器中可节省50%。
还可以通过改变循环的顺序来提高循环性能。通常,数组元素的处理顺序与任务无关,可以从最后一个开始,直到处理完第一个元素。倒序循环是编程语言中常用的性能优化方法,不过一般不太容易理解。在JavaScript 中,倒序循环可以略微提高循环性能,例如:
- for (var i=items.length; i--; ){
- process(items[i]);
- }
- var j = items.length;
- while (j--){
- process(items[j]);
- }
- var k = items.length-1;
- do {
- process(items[k]);
- } while (k--);
在上面代码中使用了倒序循环,并且在控制条件中使用了减法。每个控制条件只是简单地与零进行比较。控制条件与true 值进行比较,任何非零数字自动强制转换为true,而零等同于false。实际上,控制条件已经从两次比较减少到一次比较。将每次迭代中两次比较减少到一次可以大幅度提高循环速度。通过倒序循环和最小化属性查询,可以看到执行速度比原始版本提升了50%~60%。
与原始版本相比,每次迭代中只进行如下操作:
第1步,在控制条件中进行一次比较(i == true)。
第2步,一次减法操作(i--)。
第3步,一次数组查询(items[i])。
第4步,一次函数调用(process(items[i]))。
新循环的每次迭代中减少两个操作,随着迭代次数的增长,性能将显著提升。