本文深入解析JavaScript中循环处理与流程控制的性能优化策略。对比for、while、do-while及for-in循环的执行效率,分析减少迭代工作量的方法,如缓存数组长度。同时介绍达夫设备(Duffs Device)循环展开技术,并探讨switch、if-else与查找表的性能差异,帮助开发者编写更高效的代码。
JavaScript循环结构性能对比
循环处理是编程中最基础且高频的模式,同时也是性能优化的关键关注点。
常见的优化方案包括以下几种:
①在JavaScript的四种循环结构(for、do-while、while、for-in)中,for-in循环的性能显著低于其他三种。这是因为for-in在每次迭代时都需要同时搜索实例属性和原型属性,导致额外的开销。因此,当遍历已知且属性数量有限的列表时,建议采用更高效的遍历方式:
var props = ['prop1', 'prop2'],i = 0;
whlie(i < props.length){
precess(object[props[i++]]);
}
这种写法仅关注指定的属性,从而有效降低了循环过程中的额外开销。
对于 for、while、do-while,在 chrome 下测试,先创建大小为 1000000 的数组,每项乘 100 再叠加。
测试用例如下:
window.onload = function(){
var items = Array(1000000).join(',').split(',').map(function(item, index) {
return index;
});
console.log(forCircle())
console.log(whileCircle())
console.log(doCircle())
function forCircle(){
console.profile();
var currTime = new Date();
var tal = 0;
for(var i = 0;i < items.length; i++){
tal = tal + process(items[i]);
}
console.profileEnd();
console.log('forCircle用时:' + (new Date() - currTime) + 'ms');
return tal;
}
function whileCircle(){
console.profile();
var currTime = new Date();
var tal = 0;
var j = 0;
while (j < items.length){
tal = tal + process(items[j++]);
}
console.profileEnd();
console.log('whileCircle用时:' + (new Date() - currTime) + 'ms');
return tal;
}
function doCircle(){
console.profile();
var currTime = new Date();
var tal = 0;
var k = 0;
do{
tal = tal + process(items[k++]);
}while (k < items.length)
console.profileEnd();
console.log('doCircle用时:' + (new Date() - currTime) + 'ms');
return tal;
}
function process(item){
return item*100;
}
}
取某次测试结果:



从平均耗时来看,for循环约为8ms,while循环和do-while循环均为4ms。可见for循环在此场景下是最慢的。
减少迭代工作量的优化技巧
②减少单次迭代的工作量。具体做法包括将数组长度缓存到局部变量中,以及采用倒序遍历数组。
最常见的循环写法如下:
for(var i = 0;i < items.length; i++){
process(items[i]);
}
//
var j = 0;
while (j < items.length){
process(items[j++]);
}
//
var k = 0;
do{
process(items[k++]);
}while (k < items.length)
在该循环的每次运行中,都会执行以下操作:
①查找一次属性(items.length)
②执行一次数值比较(i < items.length)
③判断控制条件是否为真(i < items.length == true)
④执行一次自增操作(i++)
⑤执行一次数组索引查找(items[i])
⑥执行一次函数调用(process(items[i]))
如果将数组长度存储在一个局部变量中,就可以避免每次迭代都去查找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%)。总体而言,当循环次数较大时,运行时间确实有显著提升。某次测试结果如下:
未使用局部变量存储数组长度时:

使用局部变量存储数组长度时:

达夫设备与流程控制优化
③减少迭代次数,即采用“Duffs Device”(达夫设备)的循环展开技术。该技术适用于迭代次数较大的场景。
引用书中关于达夫设备的基本理念:每次循环中最多可调用8次process()函数。循环迭代次数为元素总数除以8。由于总数不一定是8的整数倍,startAt变量用于存放余数,指示第一次循环应执行多少次process()。例如,若有12个元素,第一次循环将调用process()4次,第二次循环调用8次,从而用2次循环替代了原本的12次循环。
基本模式如下:
var iterations = Math.floor(items.length/8),
startAt = items.length%8,
i = 0;
do{
switch(startAt){
case 0 : process(items[i++]);
case 7 : process(items[i++]);
case 6 : process(items[i++]);
case 5 : process(items[i++]);
case 4 : process(items[i++]);
case 3 : process(items[i++]);
case 2 : process(items[i++]);
case 1 : process(items[i++]);
}
startAt = 0;
}while(--iterations);
④基于函数的迭代(如forEach)比基于循环的迭代消耗更多性能。例如对比for循环与forEach函数迭代。
⑤优化if-else结构。通常情况下,switch比if-else执行更快;但在判断条件较多时,使用查找表(Lookup Table)比if-else和switch都要快。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持本站!