說明

本文針對一些JavaScript效能相關的文章之理論進行實際的測試與驗證,結果有些符合預期,有些則意料之外。測試環境瀏覽器如下:

Chrome:18.0.1025.168 m

Firefox:12.0

Safari:5.1.5(7534.55.3)

Opera:11.62

IE8、IE7為使用IE9切換模式

 

變數存取

JavaScript變數存取具有以下特性:

  1. 使用變數時會透過作用域鏈(Scope Chain)自動判斷其所屬範圍,會從目前區域變數查找,逐漸往外圍查找。
  2. 對變數存取屬性(點符號.)會進行查找而消耗時間。

根據以上原則,我們可以推論出一些增加效能的可能方法並實際測試:

使用區域變數,減少變數查找次數,包含作用域鏈與存取屬性,盡可能的使用區域變數。

 ChromeFirefoxSafariOperaIE9IE8IE7
存取區域變數 14.9 15.4 62.8 58.3 30 25 30
存取全域變數 13.8 17 73.9 74.1 43.7 11165.7 11208.6
存取變數屬性 15.3 12.9 80.8 74.5 45.1 40.4 43.1
存取變數屬性(window) 1392.3 1389.9 1458 1227.4 8354.5 27116.4 28762.2

執行次數 10000000,10次平均,時間單位ms

結果:使用區域變數效能最好,IE8之前的版本更為明顯。

 

由於JavaScript中的函式也是一種物件,我們依據上面的邏輯將呼叫函式也進行測試。

 ChromeFirefoxSafariOperaIE9IE8IE7
呼叫區域函式 17.5 80 159.1 126.8 58.5 65.3 58.3
呼叫全域函式 17.4 20.1 161.1 68.2 80.5 74.6 72.9
呼叫變數函式 132 88.6 136.3 166.8 74.6 68.6 77.2

執行次數 10000000,10次平均,時間單位ms

結果:測試結果發現並不如預期,反而是呼叫全域函式的效能較佳。

 

相同的道理,陣列長度屬性暫存到變數中會增加效能

 ChromeFirefoxSafariOperaIE9IE8IE7
array.length 20 13.3 60.2 63.2 44.7 41.9 41.3
var length 16.2 11.5 27.6 23.8 24.5 22.4 20.6

執行次數 10000000,10次平均,時間單位ms

結果:使用區域變數效能最好。

 

直接宣告物件屬性,會比宣告完後再設值好,而存取方式又分為屬性和關聯式陣列兩種

 ChromeFirefoxSafariOperaIE9IE8IE7
直接宣告 93.6 177.8 450.1 407 899.9 882.5 900
變數屬性 769.4 216.7 441.2 743.3 2492.7 2466.6 2493.2
關聯式陣列 791.8 236.7 1448.3 761.1 2494.9 2492.7 2486.8

執行次數 10000000,10次平均,時間單位ms

結果:直接宣告效果最好,而存取又以屬性方式存取較好,在Safari最為明顯。

 

避免使用with

使用with關鍵字時會作用域鏈會改變,增加額外的搜索時間。合併上面的表格比較

 ChromeFirefoxSafariOperaIE9IE8IE7
存取區域變數 14.9 15.4 62.8 58.3 30 25 30
存取全域變數 13.8 17 73.9 74.1 43.7 11165.7 11208.6
存取變數屬性 15.3 12.9 80.8 74.5 45.1 40.4 43.1
存取變數屬性(window) 1392.3 1389.9 1458 1227.4 8354.5 27116.4 25280
with 5949.1 10287.6 2406.9 1792.5 527.3 511.6 536.3
with包含迴圈 14070 14550.1 4823.4 3934.3 1543.4 1535.8 1553.7

執行次數 10000000,10次平均,時間單位ms

結果:使用with關鍵字效能大幅下降,且包含的範圍越大效能越差。

 

Eval is Evil

執行eval函式需要使用script引擎將字串轉換為可執行的程式,因此要避免使用eval。

 ChromeFirefoxSafariOperaIE9IE8IE7
一般 24.9 15.3 49.9 55.2 33.6 26.3 31.6
eval 6519.2 25739.1 2912.5 6055 2406.7 2189.3 2194.6

執行次數 10000000,10次平均,時間單位ms

結果:使用eval效能大幅下降。

 

相同的原理,建立Function物件,避免使用new Function來建立。

 ChromeFirefoxSafariOperaIE9IE8IE7
function() {} 170.3 1560.4 370.5 866.7 888.5 1250 1148.2
new Function 11730.8 52299.8 27790.5 N/A 7848.2 9579.8 9844.2

執行次數 10000000,10次平均,時間單位ms

結果:使用new Function效能大幅下降,其中Opera瀏覽器還有bug,記憶體爆炸無法跑完。

 

字串處理

在處理字串方面,使用不同方式來連接字串,並進行比較

 ChromeFirefoxSafariOperaIE9IE8IE7
+= 133.9 40.2 79.2 99.2 121.3 99.4 102.8
string.concat 144.2 45.4 50.8 128.4 96.8 108.2 103.2
array.join 213.7 79.2 79.7 144.5 69.2 71.2 74.8

執行次數 1000000,10次平均,時間單位ms

結果:文獻資料表示使用array.join效果最好,然而實際上,只在IE瀏覽器表現如此。

 

上面的例子是一次接一個字串或變數,然而實際上時常是多個字串或變數,測試如下

 ChromeFirefoxSafariOperaIE9IE8IE7
+= a + b 124.8 45.7 101.9 96.6 243.3 255.4 257.3
+=a
+=b
272.7 74.3 156.1 193.2 239.8 260.6 261.9
string.concat a + b 266.2 41.4 162.4 143.1 300.5 287.8 277.4

string.concat a
string.concat b

315.2 195 154.9 261.3 269.4 280.1 263.7
string.concat a, b N/A 61.1 182.1 199.6 304.8 285.9 284.2

push a + b
array.join

364.4 86.7 258.7 167.8 235.5 223.2 228.7

push a
push b
array.join

343.1 121.1 160.9 218.1 144.4 139.7 139.3

執行次數 1000000,10次平均,時間單位ms

結果:除了IE瀏覽器使用array.join效能較好,其他瀏覽器幾乎都是直接使用+= a + b就有不錯的表現,結論是直接相加是最合適的。

 

使用原生的指令

使用最基本的程式指令會比呼叫包裝好的函式功能效能還好,呼叫函式會有額外堆疊產生,以下以Math.max為例

 ChromeFirefoxSafariOperaIE9IE8IE7
Math.max(a, b) 41.7 38.9 619.2 799.3 167.9 169.5 174.8
a < b ? b : a 29.4 50.1 61.1 54.9 30.7 31.3 28.6

執行次數 10000000,10次平均,時間單位ms

結果:在大部分的瀏覽器都是使用基本的程式判斷效能較好。

 

相同的道理,使用一些JavaScript Framework雖然提供許多好用的功能,但是可能會造成效能下降,以下以jQuery為例

 ChromeFirefoxSafariOperaIE9IE8IE7
getElementById 116.1 208.4 126.5 554.9 849.8 4572.9 4394.4
jQuery("#id") 1287 6801.5 2561 1877.7 2555.7 19403.1 19273.6

執行次數 1000000,10次平均,時間單位ms

結果:使用jQuery效能明顯下降。

 

Try-Catch語句

使用Try-Catch時,執行次數會造成額外的耗時,在遇到迴圈時,應該建立在迴圈之外

 ChromeFirefoxSafariOperaIE9IE8IE7
迴圈外 65.1 35.2 63.6 499.5 40.6 42.3 39
迴圈內 74 42.5 65.8 551.3 161.9 154.7 161.1

執行次數 10000000,10次平均,時間單位ms

結果:包含的範圍在迴圈外時效果較好。

 

DOM處理

加入Element

使用JavaScript動態加入Element時,直接加入會造成Reflow,所以應該先在程式中處理完成後最後在加入,例如先使用createDocumentFragment來存放即將加入的Element

結果:由於這部分測試程式的完成時間和ui實際reflow完成的時間不一致,故沒有精確的時間記錄,但測試結果效能排名依序 innerHTML > createDocumentFragment > 直接加入,直接修改innerHTML最佳。

 

修改CSS

由於修改文件中的Element的CSS屬性會造成reflow和repaint,當要修改多項屬性時,應該使用className一次性修改

 ChromeFirefoxSafariOperaIE9IE8IE7
直接修改 122.8 418.1 254.4 236.2 638.6 516.7 1510.2
cssText 567.8 7904.6 495.6 57 522.4 624.8 1695.1
setAttribute("style", ...) 466.4 49.7 473.9 69.8 538 715.4 no support
className 27 42.7 44.2 51.8 226.2 352.6 1489.4

執行次數 100000,10次平均,時間單位ms

結果:使用修改className效果最好,然而實務上屬性時常是變動值,無法預先建立class,而不得已要修改element的css時,以setAttribute在大多數瀏覽器表現最好,但IE7不支援此方法。

 

承上,故有理論提出將Element隱藏或複製在程式中後處理,以避免reflow

 ChromeFirefoxSafariOperaIE9IE8IE7
直接修改 101.5 463 253.8 227 632.8 513 1509.5
display: none 104.6 457.3 258.7 230.1 430.9 517.1 1362
cloneNode 97 266 252.1 229.6 1658.6 1703.9 891.5

執行次數 100000,10次平均,時間單位ms

結果:cloneNode只在Firefox效能有提升,在IE9和IE8反而下降;而隱藏只在IE9有作用,總和來看,隱藏的方式效能是最平均。

 

變數名稱與註解

有些文章中提到,變數的名稱太長和註解會造成效能下降,然而實際上測試完全沒有影響,可以放心的使用。

文章標籤
創作者介紹

小殘的程式光廊

emn178 發表在 痞客邦 PIXNET 留言(0) 人氣()