JavaScript性能优化
循环
- For循环
1 | for (var i = 0; i < 10; i++) { |
- For/In循环
1 | // TODO 不推荐,相同的迭代次数,性能是其他三种的1/7 |
- While循环
1 | while (condition) { |
- Do/While循环
1 | do { |
1 & 3为前测型循环,每次循环时,先进行条件检测。
2 & 4为后测型循环,先执行一次内容,后进行条件监测。
这四种循环中,不建议使用For/In循环,其在相同的条件与规模下,执行时间是其他三种的七倍,效率较低。
减少迭代的工作量
将再循环中执行的相同语句,提到循环外执行,如:
1 | var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; |
可以优化为:
1 | var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; |
arr.length
从每次循环都要执行一次,共执行10次,优化为总共执行一次。
条件语句
1 | if (condition) { |
1 | switch (foo) { |
- switch 表达式比if/else表达式的可读性更好
- 大多数情况下switch比if/else运行速度更快
- 条件增加时,if/else的性能负担增加
综上,更倾向于在条件数量较少时使用 if/else在条件数量较大时使用switch, 通常来说,if/else适用于判断两个离散值或
几个不同的值域,当判断的离散值过多时则 推荐使用switch语句。
优化if-else语句
目标:最小化到达正确分支前所需要判断的条件数量
1 | if (a < 10) { |
如果是a
的值整数的离散值的话,使用下面的程序:
1 | // bad example |
更进一步的优化可以使用二分法判断:
1 | // good example, binary search |
使用二分法把离散的值分成不同值域的一系列区间,逐步缩小范围。 当值的范围均匀分布在0-10之
间时,代码运行的效率增加。如果分布不均匀,可以适当调整判断的位置,从而最小化判断次数。
递归算法
使用递归函数的潜在问题是终止条件不明确或者缺少终止条件,从而导致程序陷入死循环,递归函数还可能遇到浏览器的调用栈大小限时。
1 | // 斐波那契额数列函数 |
递归的调用:
1 | function foo() { |
1 | // 隐伏式递归 |
在第二种递归模式中,两个函数相互调用形成死循环,在大型程序中很难排查,如果可以使用迭代的方式解决,尽量不要使用递归算法。
在编程中优化
避免双重求值
执行一段义字符串表示的程序,有几种:eval()
, Function()
, SetTimeout()
, SetIntval()
在JavaScript代码中执行另一段JavaScript代码时,都会导致双重求值的性能消耗。此时代码首先会已正
常的方式求值,然后再执行过程中对包含于字符串中的代码发起另一个求值运算。 消耗的时间存在巨大差异是因为每次调用
***eval()是都要创建一个新的解释器/编译器实例。非特殊情况没必 要且应尽量避免使用eval()和Function(),至于
setTimeout()和setInterval()***建议穿入函数最为参数值。
使用Object/Array直接量
创建一个对象:
1 | var MyObj = new Object(); |
对象属性和数组项的数量越多,使用直接量的好处就越明显。
使用定时器让出时间片段
在JavaScript操作UI/DOM时,若无法在短时间内完成,最理想的方式是让出UI控制权。是UI可以更新,让出控制权意味着停止执行代码,使UI线程有机会更新。
1 | var button = document.getElementByID("my-button") |
- 如果anotherMethod在250ms内完成,则DOM Opration会在250ms时执行
- 如果anotherMethod在T(>250ms)时完成,则DOMOpration会在T+250时执行
浏览器会有一个脚本执行时间限制,但是如果设置定时器,SetTimeout会刷新脚本时间显示,是一个低成本的跨浏览器兼容解决方案。
JavaScript性能优化