众所周知,常用的循环语句有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值比较,任何非零数会自动转换 为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由于时间太短不显示)
当数据量达到100w时,firefox和chrome下的结果都如人所愿,但是也略微有所不同。ff下的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; }
swtich-case语句让代码显得可读性更强,而且swtich-case语句还有一个好处是如果多个value值返回同一个结果,就不用 重写return那部分的代码。一般来说,当case数达到一定数量时,swtich-case语句的效率是比if-else高的,因为switch- case采用了branch table(分支表)索引来进行优化,当然各浏览器的优化程度也不一样。
除了if-else和swtich-case外,我们还可以采用查找表。
var results = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10]; //return the correct result return results[value];
当数据量很大的时候,查找表的效率通常要比if-else语句和swtich-case语句高,查找表能用数字和字符串作为索引,而如果是字符 串的情况下,最好用对象来代替数组。当然查找表的使用是有局限性的,每个case对应的结果只能是一个取值而不能是一系列的操作。
小结:
- 当只有两个case或者case的value取值是一段连续的数字的时候,我们可以选择if-else语句
- 当有3~10个case数并且case的value取值非线性的时候,我们可以选择switch-case语句
- 当case数达到10个以上并且每次的结果只是一个取值而不是额外的JavaScript语句的时候,我们可以选择查找表
相关推荐
1. **基础语法**:包括变量、数据类型、运算符、流程控制语句(如if-else、switch、for循环和while循环)以及函数定义和调用。 2. **作用域和闭包**:讲解了如何管理变量的作用域,以及闭包如何为JavaScript提供...
- **流程控制**:使用条件语句、循环等语法结构来控制程序的执行流程。 **技术细节:** - 在JavaScript中,合理的算法设计可以显著提高程序性能。 - 循环、递归等基本流程控制结构的应用技巧对于优化代码至关重要。...
本章涵盖了基础算法和流程控制结构,如循环、条件语句等,以及它们在JavaScript中的实现。讨论了如何优化循环性能,避免不必要的计算,以及合理选择控制流工具来提高代码执行效率。 第五章 “字符串和正则表达式”...
通过阅读《高性能JavaScript编程》中英对照版和英文版,开发者可以全面提升对JavaScript性能优化的理解,为构建更快、更稳定的Web应用打下坚实基础。无论是初级开发者还是经验丰富的专业人士,这本书都能提供宝贵的...
1. **基础语法与数据类型**:JavaScript的基础包括变量、数据类型(如基本类型和引用类型)、操作符、流程控制语句以及函数。理解这些概念对于编写高效代码至关重要。 2. **作用域和闭包**:了解JavaScript的作用域...
4. **流程控制**:`if...else`用于条件判断,`for`、`while`用于循环,`switch...case`处理多分支选择。 5. **函数**:`function`关键字用于定义函数,可以有参数和返回值。ES6新增了箭头函数(`=>`),语法更简洁...
10. **性能优化**:理解V8引擎的工作原理,学习如何编写高性能的JavaScript代码,如减少内存泄漏、优化循环等。 11. **ES6新特性**:箭头函数、类、解构赋值、模板字符串、let/const、生成器、async/await等,这些...
JavaScript作为Web开发中的重要语言,是前端开发的核心技术之一,尤其在构建动态、交互式的...通过系统学习,开发者不仅能理解JavaScript的核心原理,还能运用到实际项目中,打造出具有高性能和良好用户体验的Web应用。
1. **基础概念**:讲解JavaScript的基本语法,如变量、数据类型(包括基本类型和引用类型)、运算符、流程控制(条件语句和循环语句)以及函数的使用。 2. **对象与数组**:深入理解JavaScript的对象机制,包括属性...
JavaScript的基础包括变量声明(var、let、const)、数据类型(如字符串、数字、布尔值、null、undefined、对象、数组、符号、大整数)、运算符(算术、比较、逻辑、位运算、三元运算)、流程控制(条件语句、循环...
首先,JavaScript的基础部分涵盖了变量、数据类型(包括基本类型和引用类型)、操作符、流程控制(如条件语句和循环)、函数以及作用域。理解这些基础知识对于编写任何JavaScript代码都是至关重要的。变量用于存储...
书中首先会讲解JavaScript的基本语法,包括变量、数据类型、运算符、流程控制(条件语句与循环)以及函数。这些基础知识构成了JavaScript程序的基础结构,为后续的高级主题打下坚实的基础。 接下来,深入到对象和...
接着,我们将讨论JavaScript的控制结构,包括条件语句(如if...else和switch)和循环(如for、while和do...while),它们是程序逻辑的基础。同时,了解函数的定义与调用,特别是箭头函数和闭包,能帮助你编写更加...
2. **控制结构**:JavaScript提供了条件语句(if...else)和循环语句(for、while、do...while)来控制程序的流程。这些结构在编写游戏逻辑时起着关键作用。 3. **函数**:函数是一段可重复使用的代码块,可以接受...
1. **基础语法**:包括变量声明(var、let、const)、数据类型(如字符串、数字、布尔值、null、undefined、对象、数组等)、运算符、流程控制(条件语句、循环语句)。 2. **函数与闭包**:函数作为第一类公民,...
- 控制流程:包括条件语句(if...else)和循环(for、while),用于处理程序逻辑。 - 函数:定义和调用函数,是代码复用的基础。 - DOM操作:JavaScript可以用于操作HTML文档对象模型(DOM),例如修改元素内容、...
2. **基础语法**:JavaScript的基础包括变量声明(var、let、const)、数据类型(如字符串、数字、布尔、null、undefined)、操作符(算术、比较、逻辑)、流程控制(条件语句if...else、循环for、while等)。...
1. **基础语法**:变量声明(var, let, const)、数据类型(原始类型和引用类型)、运算符(算术、比较、逻辑、三元)、流程控制(条件语句、循环语句、开关语句)、函数(声明式与表达式、作用域、闭包)等。...
此外,流程控制语句(如if...else、for、while)帮助我们根据条件执行不同的代码块,实现程序的逻辑流程。 接着,深入学习函数和对象。JavaScript中的函数不仅是代码的复用单元,还可以作为值传递,甚至可以作为...
1. **基础语法**:讲解了变量、数据类型、运算符、流程控制(如条件语句、循环)、函数等基本元素,帮助初学者建立JavaScript编程的基本框架。 2. **对象和原型**:深入探讨了JavaScript的面向对象特性,包括对象...