高效編寫JavaScript程式碼的技巧

才智咖 人氣:1.81W

如何能高效的編寫出優秀的JavaScript程式碼,有哪些技巧?下面本站小編為你介紹!

高效編寫JavaScript程式碼的技巧

原則

不要做任何優化除非的確需要優化

任何的效能優化都必須以測量資料為基礎,如果你懷疑程式碼存在效能問題,首先通過測試來驗證你的想法。

效能優化三問

我還能做哪些工作從而讓程式碼變得更有效率?

流行的JavaScript引擎通常會做哪些優化工作?

哪些優化是JavaScript引擎不能做的,垃圾回收器是否能清理我們期望清理的?

對JavaScript引擎的深入瞭解有助於我們編寫高效的JavaScript程式碼,但不要只針對某一特定引擎做效能優化。

V8的幾個關鍵概念

基礎編譯器,解析你的JavaScript程式碼並生成Native Machine Code執行,而不是執行位元組碼或是直接對JavaScript解釋執行。

在V8中,物件以object model的形式存在。物件在JavaScript中是以關聯陣列的形式存在,但V8採用的是Hidden

Classes——一種對查詢操作進行了優化的內部型別系統。

執行時探查器監視執行中的系統,並識別出Hot functions,即是耗用了較長時間的程式碼

優化編譯器重新編譯並優化由執行時探查器識別出來的Hot程式碼

V8支援反優化,優化編譯器能夠發現過度優化的程式碼並對其進行處理

V8有自己的垃圾回收器

垃圾回收垃圾回收是一種記憶體管理機制,垃圾回收器會嘗試清理掉不再被使用的物件,並回收記憶體。

在絕大多數情況下都不需要手動解除引用

你不可能強制垃圾回收器工作

刪除引用的誤區

儘可能不要使用delete,在下面的列子中,delete 帶來的弊遠遠大於利

var o = { x: 1};delete o.x;

主要的原因是為了避免在執行時修改Hot物件的結構,因為固定的物件結構有助於JavaScript引擎對其進行優化,而delete會導致物件結構改變。

另外一個誤區是將物件設定為null,將物件設定為null不會刪除物件,只是將物件指向null,這要好過採用delete,但通常也是不必要的。

全域性變數在整個頁面生命週期中都是不會被清理的,無論頁面開啟多長時間,除非是重新整理頁面或者轉到其他頁面。區域性變數(Function-scoped)在方法執行完後,且沒有被引用的情況下將會被回收。

所以,請儘量避免使用全域性變數。

經驗法則為了使垃圾回收器儘早回收物件,不要保持不必要的物件引用。

比手動解除引用更好的方法是將物件放在合適的變數域中,能用區域性變數就不要採用全域性變數

當事件監聽不再需要時,請解除事件繫結,尤其是當事件繫結的DOM物件被刪除時

如果有使用本地快取,請確保有合適的清理機制(比如時效機制),從而避免大量無用的資料儲存。

方法 (Function)

如前面所說,垃圾回收器只有在物件不可觸及的時候才會對其做回收處理。考慮如下兩個列子:

function foo(){

var bar = new LargeObject()

Call();}

function foo(){

var bar = new LargeObject()

Call();

return bar;}var b = foo();

在第一個例子中,bar指向的物件會在方法執行完畢後處於可回收狀態;在第二個列子中,由於在區域性變數外維護了一個全域性變數b,bar指向的物件無法被回收。

閉包 (Closures)

當一個方法返回一個內部方法時,被返回的內部方法能訪問外部方法的區域性變數域即使外部方法已經執行完畢。

function sum(x){

function sumIt(y){

return x + y;

}}var sumA = sum(4);var sumB = sumA(3);

在上面的例子中,sumIt方法即使處於sum的區域性變數域中,但由於存在一個sumA全域性變數,在sum執行完畢後也無法被回收。再看兩個例子

var a = function(){

var largeObj = new LargeObject();

return function(){

return largeObj;

}}();var a = function(){

var smallObj = new SmallObj();

var largeObj = new LargeObj();

return function(n){

return smallObj;

}}();

第一個例子中,largeObj可以通過變數a訪問,因此不可被回收;在第二個例子中,方法一旦執行完畢,largeObj就無法被訪問了,因此處於可回收狀態。

定時器 (Timer)在setTimeout / setInterval方法中的引用,只有當定時器執行完成後才能被回收。

V8優化小貼士

某些行為會導致V8停止優化工作,比如try-catch,為了能弄清哪些程式碼可以被優化,哪些不能,你可以在V8命令列工具中使用—trace-opt 獲得有用的資訊。

如果你在意速度,那就儘可能保證你的方法是”單形的(monomophic)"

不要做類似如下的嘗試

function add(x, y){

return x+y;}add(1,2);add('a','b');add(my_custom_object, undefined);

不要載入沒有被初始化或者已被刪除的元素,儘管在輸出上沒有不同,但卻會讓程式碼變得更慢

不要寫大方法,因為他們很難被優化。

物件還是陣列, 如何選擇?

如果儲存的是大量數字,或者是相同型別的物件列表,採用陣列;

如果根據語義你需要一個有很多屬性的物件,那就採用物件,在記憶體利用方面這會很高效,同時也很快;

無論是陣列還是物件,採用整數索引都最快的.。

var sum = 0;for (var x=0; x

sum + = arr[x]oad;}

var sum = 0;for(var x in obj){

sum += obj[x]oad;}

var sum = 0;for(var x=0; x<1000,++x){

sum += obj[x]oad;}

var sum = 0;var keys = (obj);for(var x=0; x

sum += obj[keys[x]]oad;}

在上面的四段程式碼中,第一段和第三段速度比第二段和第四段要快很多。其中,第一段程式碼執行最快,最後一段程式碼執行最慢。

相比陣列中的元素,物件的屬性在結構上相對複雜。在引擎層面,記憶體中越是簡單的結構越容易被優化,尤其是包含數字的陣列。因此,如果你需要向量,採用陣列而不是一個包含x,y, z屬性的物件會有更優的效能表現。