javascript
《高性能JavaScript》第四章 算法和流程控制
4.1 循環
4.1.1 循環的類型
主要有四種循環類型:for、while、do-while和for-in。
-------------------------------------------------------------------- 注:如果你對python感興趣,我這有個學習Python基地,里面有很多學習資料,感興趣的+Q群:895817687 --------------------------------------------------------------------// 1、for循環:初始化、前測條件、后執行體和循環體組成。for (var i=0; i < 10; i++){// 循環體}// 2、while循環:前測條件和循環體組成。var i = 0;while(i < 10){// 循環體i++;}// 3、do-while循環:循環體和后測條件組成。var i = 0;do {// 循環體} while (i++ < 10);// 4、for-in循環:可以枚舉任何對象的屬性名。for (var prop in object){// 循環體}注意:for循環初始化的var語句會創建一個函數級別的變量,而不是循環級。由于JavaScript只有函數級作用域,因此for循環中定義一個新變量相當于在循環體外定義一個新變量。
4.1.2 循環性能
循環的性能提升主要由這幾個方面入手:
1、循環的類型:除了for-in循環比其他三種循環明顯要慢外,其他幾種性能都差不多。所以除非明確需要迭代一個屬性數量未知的對象,否則避免使用for-in循環;
2、減少迭代的工作量:將需要多次查找的屬性存入局部變量中重復使用,并使用倒序循環;
3、減少迭代次數:使用“達夫設備(Duff’s Device)”模式限制迭代次數。Duff’s Device背后的基本理念:每次循環最多可調用8次process(),循環迭代的次數為總數除以8。
// 原始版Duff’s Devicevar 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); // 升級版Duff’s Device:盡管這種方式用兩次循環代替之前一次循環,但移除了循環體中的switch語句,速度比原始更快。var i = items.length % 8;while(i){process(items[i--]);}i = Math.floor(items.length / 8);while(i){process(items[i--]);process(items[i--]);process(items[i--]);process(items[i--]);process(items[i--]);process(items[i--]);process(items[i--]);process(items[i--]);}性能優化:迭代次數<1000,和常規循環結構比性能提升微不足道;迭代次數>1000,Duff’s
Device模式性能會有明顯提升。在5000次迭代中,性能比常規提升70%。
4.1.3 基于函數的迭代
ECMA-262標準第四版介紹了本地數組對象的一個新方法forEach()。此方法遍歷一個數組的所有成員,并在每個成員上執行一個函數。在每個元素上執行的函數作為 forEach()的參數傳進去,并在調用時接收三個參數,它們是:數組項的值,數組項的索引,和數組自身。
items.forEach(function(value, index, array){process(value);});性能優化:在所有情況下,基于循環的迭代比基于函數的迭代快8倍。因此在運行速度嚴格要求時,基于函數的迭代不是合適的選擇。
4.2 條件語句
4.2.1 if-else對比switch
多數情況下,switch比if-else運行的要快,但只有條件數量很大時才快的比較明顯。這兩句的主要性能區別是:當條件增加時,if-else性能負擔增加的程度比switch要多。
性能優化:if-else適用于判斷兩個離散值或幾個不同的值域;當判斷多個離散值時,switch語句是更佳選擇。
4.2.2 優化if-else
優化目標:最小化到達正確分支前所需判斷的條件數量。
優化原則:
將最可能出現的條件放在首位,最小概率出現的的條件放在末尾;
使用if-else嵌套:
嵌套過程中使用二分法把值域分成一系列區間,逐步縮小范圍。
從而減少條件判斷次數。
- 反例
- 正例
4.2.3 查找表
- 描述
JavaScript中可以使用數組和普通對象來構建查找表,特別是在條件語句數量很大的時候,性能比條件語句快很多。
- 原因
當你使用查找表時,必須完全拋棄條件語句。這個過程變成數組項查詢或者對象成員查詢。主要優點:不用書寫任何條件語句,即便候選值數量增加時,也幾乎不會產生額外的開銷。
- 反例
- 正例
4.3 遞歸
4.3.1 調用棧限制
JavaScript引擎支持的遞歸數量與JavaScript調用棧大小直接相關。如果超出了瀏覽器的調用棧限制,就會拋出調用棧溢出的異常。
4.3.2 遞歸模式
遞歸一般有兩種模式:直接遞歸模式和隱伏模式。第二種模式在出現問題時,很難定位。
// 1、直接調用模式function recurse(){recurse();}recurse();// 2、隱伏模式function first(){second();}function second(){first();}first();4.3.3 迭代
- 描述
任何遞歸能實現的算法,迭代也能實現。
- 原因
使用優化后的循環替代長時間運行的遞歸函數可以提升性能,因為運行一個循環比反復調用一個函數的開銷要少很多。
- 反例
- 正例
4.3.4 Memoization
Memoization是一種避免重復工作的方法,它緩存前一個計算結果后供后續計算使用。
以階乘函數為例:
使用Memoization后,將第一次計算的結果緩存起來,后期再調用的時候,直接從緩存中讀取結果。
function memfactorial(n){if (!memfactorial.cache){memfactorial.cache = { "0": 1, "1": 1 };}if (!memfactorial.cache.hasOwnProperty(n)){memfactorial.cache[n] = n * memfactorial (n-1);}return memfactorial.cache[n];}總結
以上是生活随笔為你收集整理的《高性能JavaScript》第四章 算法和流程控制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《高性能JavaScript》第三章 D
- 下一篇: 《高性能JavaScript》第五章 字