撰寫高效能的 Javascript 小技巧
這篇文章將會介紹簡單的技巧來優化我們的程式碼讓 Javascript 編譯的過程更具效率,最終我們的程式碼可以執行的更加快速。
特別是當您在遊戲類型的專案上發現掉禎或記憶體回收機制(garbage collector)遇到大量資料無法被回收的情況。這些技巧可以協助我們增加程式碼的效能。
單態(Monomorphism)
當我們定義一個函式搭配兩個參數的時候,如果函數的參數型別,數量,傳回的型別改變編譯器會遵循我們的指令,但效能便會開始下降。因為一般程式會預期一個單態的資料結構且相同的參數。
1 | function example(a, b) { |
展開(Unfolding)
編譯器可以在編譯時期解析變數的值並且在最好的情況下可以將其展開,盡可能在程式實際執行前解析出其資料。
常數和變數只要是不需要等到執行時期才能夠計算的值都會被展開。
1 | const a = 42; // 能夠被簡單的展開 |
內嵌(Inlining)
JIT 編譯器可以知道我們程式碼哪些段落特別常被執行。透過拆分 function 為更小的片段程式碼,就可以在編譯時期將其內嵌並追蹤特別常被使用的 function 同時提升效能。
1 | // Likely gets inlined 可能被內嵌的條件 |
宣告
避免宣告函式/閉包和物件在經常被調用的任務中。物件會被堆進 heap 這會影響記憶體回收造成回收上的困擾。
1 | // 糟糕 |
引數(Arguments)
調用函式是很耗費效能的。盡可能減少直接使用 arguments 並且不要在函式內部修改它們
1 | function mul(a, b) { |
資料型別(Data Types)
盡可能使用 Number 和 Boolean 他們相對於其他資料型別快非常多。
1 | const ROBOT = 0; |
嚴格與轉換型別運算子(Strict and abstract operators)
盡量使用 ===
而不是 ==
由於嚴格比對模式下編譯器不需要執行額外的轉換因此效能會比較好。
致命傷
下列功能盡可能避免使用會造成嚴重的效能問題
- eval
- with
- try/catch
物件
物件實例通常會分享一個隱藏且相同的類別,注意在替物件實例加上新的屬性時會建立一個新的隱藏類別,這件事對編譯器來說挺複雜的。
1 | // 隱藏的類別 'hc_0' |
迴圈
盡量使用單一型別的陣列。減少使用 for .. in
來遍歷物件,效能很差。
在迴圈中 continue
和 break
效能比 if 條件式要快。盡可能讓迴圈要處理的資料在外頭。另外使用 ++i
而不是 i++
這可以得到一點點的效能提升。
1 | let badarray = [1, true, 0]; // 糟糕, 盡量不要混合型別 |
參考資料
撰寫高效能的 Javascript 小技巧
https://andyyou.github.io/2016/12/19/write-efficient-javascript/