代码的多少与运行速度并无很紧密的关联。影响代码运行速度的主要因素是:代码的组织结构和解决问题的具体思路。
循环
在大多数编程语言中,代码执行时间大部分消耗在循环中。
循环是最常见的编程模式之一,也是提升性能必须关注的要点之一。
循环的类型
JavaScript中有四种循环类型。
- 标准的for循环:
for (var i=0; i < 5; i++) {
// do sth
}
for循环是JavaScript中最常用的循环结构。它由四部分组成:初始化、前测条件、后执行体、循环体。
当代码遇到for循环之后,先运行初始化代码,然后进入前测条件。如果前测条件为true,则执行循环体。循环体执行之后,后执行代码开始运行。
- while循环
var i = 0;
while(i < 10) {
// do sth
i++;
}
while循环由一个前测条件和一个循环体构成。
在循环体运行前,先计算前测条件。若计算结果为true,则执行循环体;
任何for循环都能改写为while循环。
- do-while循环
do-while循环是JavaScript中唯一一种后测循环。它由循环体和后测条件构成。
var i = 0;
do {
// 循环体
} while (i++ < 10)
在do-while循环中,循环体会至少执行一次。然后再由后测条件决定是否再次执行。
- for-in循环
for-in循环可以枚举任意对象的对象名:
for (var prop in object) {
// do sth
}
循环体每次运行时,prop变量被赋值为object的一个属性名,直到所有的属性遍历完才返回。包括对象从原型链继承来的属性。
循环性能
在上述四种循环类型中,只有for-in循环明显要慢。
由于每次迭代操作会同时搜索实例或原型属性,for-in循环的每次迭代都会产生更多开销。
对比相同次数的循环,for-in循环最终只有其他类型的速度的1/7。所以,除非明确需要迭代一个属性数量未知的对象,否则应该避免使用for-in。
如果循环类型与性能无关,那么可以从下面两点出发去优化:
- 每次迭代处理的事物
- 迭代的次数
减少迭代的工作量
一个提升循环整体速度的好方法是限制每次循环耗时操作的数量
先来看一个示例代码:
// 未优化之前的版本
for (var i=0; i < items.length; i++) {
// 调用一个方法
process(items[i])
}
在上面的这个示例中,每执行一次循环,需要做的工作如下:
- 在控制条件中查找一次属性(items.length)
- 在控制条件中执行一次比较(i < items.length)
- 查看控制条件的结果是否为true
- 一次自增操作(i++)
- 一个数组查找(items[i])
- 一次函数调用
代码的执行速度很大程度上取决于函数调用process()的耗时。但是较少每次迭代的操作次数也能很大程度上提高循环性能。
优化循环的第一步就是减少对象成员及数组项的查找次数。
前面的例子中每次循环都要查找items.length,这样操作很耗时。由于这个值在运行中并未改变,可以把它存到一个局部变量里面。然后在控制好条件中使用这个变量:
// 最小化属性查找
for (var i=0, len=items.length; i < len; i++) {
process(items[i])
}
经过上面的优化后,根据数组的长度,在大多数浏览器中能节省25%的时间。
还可以通过倒序循环来提高循环性能。
// 减少属性查找并倒序
for (var i=items.length; i--; ) {
process(items[i])
}
上面的示例使用了倒序循环,并把减法操作放到控制条件中。
现在每个控制条件只是简单的和0作比较。
控制条件从两次比较(迭代数少于总数吗?它是否为true?)减少到一次比较(它是true吗?)。
经过上面两种优化后,执行速度大约比原始版本快了50%-60%。
未完。
网友评论