高性能JavaScript循环语句和条件语句

作者:听澜

时间:2026-10-29

浏览:387

本文深入分析JavaScript中循环与条件语句的性能差异。针对循环,探讨倒序迭代对for、while及do-while的提升效果,并对比for-in与forEach的开销。针对条件判断,解析if-else、switch-case及查找表的适用场景,通过二叉树查找与分支表机制提供优化策略,帮助开发者在极端性能需求与代码可读性之间做出权衡。

文章目录

循环语句性能分析

一、循环语句 常见的循环结构包括for、while、do-while、for-in以及forEach。除了for-in和forEach的性能相对较低外,开发者在前三者之间的选择通常基于具体需求而非性能考量。本文将通过测试对比它们各自的性能表现,探讨在极端性能要求下可行的优化手段。

首先分析为何for-in和forEach的执行速度较慢。for-in主要用于遍历对象属性,由于每次迭代需同时搜索实例自身属性及原型链上的属性,导致效率较低。而forEach基于函数迭代(需注意所有IE版本均不支持,若需兼容可使用jQuery等库),其性能瓶颈主要源于对每个数组项调用外部方法所产生的额外开销。

接下来考察每次迭代中for、while及do-while循环的具体执行过程。


var length = items.length;
for(var i = 0; i < length; i++)
 process(items[i]);

var j = 0;
while(j < length) 
 process(items[j++]);

var k = 0;
do {
 process(items[k++]);
} while(k < length);

在上述每种循环结构中,每次执行循环体时均会执行以下操作:

  • 一次控制条件中的数值大小比较(i < length)
  • 一次判断控制条件结果是否为true的比较(i < length === true)
  • 一次自增操作(i++)
  • 一次数组元素查找(items[i])
  • 一次函数调用process(items[i])

通过调整数组遍历顺序,可以提升循环性能:


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--);

本例采用倒序循环,并将减法操作整合至循环条件中。此时,控制条件仅与0进行比较。由于任何非零数在布尔上下文中自动转换为true,而零值等同于false,控制条件的比较次数从两次(判断迭代数是否少于总数?是否为true?)减少为一次(是否为true?)。每次迭代的比较次数由两次降至一次,从而进一步提升了循环速度。

性能测试:

实际情况是否如此?通过浏览器验证可知。测试代码较为简单,针对8种不同情况封装了8个函数(在Firefox下若不添加定时器则无法打印profiles信息,具体原因不明):


// init array
var a = [];
var length = 10;
for(var i = 0; i < length; i++)
 a[i] = 1;

function for_in() {
 var sum = 0;
 for(var i in a) 
  sum += a[i];
}

function for_each() {
 var sum = 0;
 a.forEach(function(value, index, array) {
  sum += value;
 });
}

function for_normal() {
 var sum = 0;
 for(var i = 0; i < length; i++)
  sum += a[i];
}

function for_reverse() {
 var sum = 0;
 for(var i = length; i--; )
  sum += a[i];
}

function while_normal() {
 var sum = 0;
 var i = 0;
 while(i < length) 
  sum += a[i++];
}

function while_reverse() {
 var sum = 0;
 var i = length;
 while(i--) 
  sum += a[i];
}

function do_while_normal() {
 var sum = 0;
 var i = 0;
 do {
  sum += a[i++];
 } while(i < length);
}

function do_while_reverse() {
 var sum = 0;
 var i = length - 1;
 do {
  sum += a[i];
 } while(i--);
}

setTimeout(function() {
 console.profile();
 for_in();
 for_each();
 for_normal();  
 for_reverse();
 while_normal();
 while_reverse();
 do_while_normal();
 do_while_reverse();
 console.profileEnd();
}, 1000);

当数组长度为100时,Firefox下的结果与预期相符:for-each和for-in效率较低,倒序遍历比正序遍历效率略有提升(Chrome下的profiles因耗时过短而未显示)。

当数据量达到100万时,Firefox和Chrome下的结果均符合预期,但存在细微差异。在Firefox中,for-in的表现优于for-each;而在Chrome中,for-in表现较差并直接发出警告。尽管倒序迭代性能略有提升,但提升幅度有限,且降低了代码的可读性。

循环语句性能分析

循环语句性能分析

小结:

  • 倒序迭代确实能略微提升代码性能,但牺牲了代码可读性。除非追求极致的性能优化,否则不建议使用。
  • 遍历数组时,若可使用普通循环,应避免使用for-in和for-each。

条件语句优化策略

二、条件语句 常见的条件语句包括if-else和switch-case。那么,何时使用if-else,何时使用switch-case呢?

首先看一个简单的if-else语句代码:


if (value == 0){
  return result0;
} else if (value == 1){
  return result1;
} else if (value == 2){
  return result2;
} else if (value == 3){
  return result3;
} else if (value == 4){
  return result4;
} else if (value == 5){
  return result5;
} else if (value == 6){
  return result6;
} else if (value == 7){
  return result7;
} else if (value == 8){
  return result8;
} else if (value == 9){
  return result9;
} else {
  return result10;
}

在最坏情况下(value=10),可能需要执行10次判断才能返回正确结果。如何优化这段代码?一个显而易见的策略是将最可能的取值提前判断,例如假设value最可能等于5或10,则将这两条判断前置。然而,通常我们并不知晓“最可能的选择”,此时可采取二叉树查找策略进行性能优化。


if (value < 6){
  if (value < 3){
    if (value == 0){
      return result0;
    } else if (value == 1){
      return result1;
    } else {
      return result2;
    }
  } else {
    if (value == 3){
      return result3;
    } else if (value == 4){
      return result4;
    } else {
      return result5;
    }
  }
} else {
  if (value < 8){
    if (value == 6){
      return result6;
    } else {
      return result7;
    }
  } else {
    if (value == 8){
      return result8;
    } else if (value == 9){
      return result9;
    } else {
      return result10;
    }
  }
}

经过上述优化,最多仅需进行4次判断即可,显著提高了代码性能。这种优化思路类似于二分查找,与二分查找相似之处在于,只有当value值为连续数字时才能进行此类优化。然而,这种写法不利于维护,若需增加一个或多个条件,则需重写大量代码。此时,switch-case语句便显示出其优势。

使用switch-case语句重写上述代码:


switch(value){
  case 0:
    return result0;
  case 1:
    return result1;
  case 2:
    return result2;
  case 3:
    return result3;
  case 4:
    return result4;
  case 5:
    return result5;
  case 6:
    return result6;
  case 7:
    return result7;
  case 8:
    return result8;
  case 9:
    return result9;
  default:
    return result10;
}

switch-case语句使代码更具可读性。此外,若多个value值返回相同结果,无需重复编写return部分的代码。一般而言,当case数量达到一定规模时,switch-case的执行效率高于if-else,因为switch-case采用了branch table(分支表)索引进行优化,尽管各浏览器的优化程度存在差异。

除if-else和switch-case外,还可采用查找表。


var results = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10];

//return the correct result
return results[value];

当数据量较大时,查找表的效率通常高于if-else和switch-case。查找表支持以数字或字符串作为索引;若使用字符串,建议用对象代替数组。当然,查找表的使用存在局限性:每个case对应的结果只能是一个取值,而不能是一系列操作。

小结:

  • 当仅有2个case,或case的value取值是一段连续数字时,可选择if-else语句。
  • 当有3至10个case,且case的value取值非线性时,可选择switch-case语句。
  • 当case数量超过10个,且每次结果仅为一个取值而非额外的JavaScript语句时,可选择查找表。

以上就是本文的全部内容,希望对大家的学习有所帮助。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章