在google,網頁呈現速度慢500毫秒將丟失20%的流量;在yahoo!,慢上400毫秒將丟失5%-9%的流量;在亞馬遜(Amazon),慢上100毫秒將丟失1%的交易量...這是速度絕對成敗的web時代,天下武學唯快不破,相信沒人嫌棄網頁打開速度太快吧!
那么要讓網頁速度更快,我們應該如何優化,應該保持怎樣的編程習慣,讓代碼運行的更加流暢?在這里,鄭重推薦《高性能網站建設指南》和《高性能網站建設進階指南》,完全掌握這兩本書的內容對你的前端生涯將是受益匪淺。
言歸正傳,javascript是解釋型語言,執行速度自然比不上編譯型語言,另外,計算機系統分配給瀏覽器的資源有限,分給web應用的那就更少。因此,javascript程序性能比不上其他編譯型語言,然而,2005年之后,瀏覽器開發商對javascript執行性能進行了大量優化,javascript的性能已經是大步前進。不過,仍然有許多地方可以提高整體代碼的性能。
function updateUI(){ var divs = document.getElementsByTagName("div"); for(var i = 0; i < divs.length; i++){ divs[i].innerHTML = document.title + " div " + i; } }
看看上訴代碼,有哪些地方需要優化的?這里提醒一句,切忌過早的優化,這會讓你束手束腳,並且極度不利於后續的開發,成為萬惡之源!
使用local變量
上面的代碼中,有兩個地方使用到document全局對象,而訪問全局對象需要遍歷整個作用域鏈,自然比訪問局部變量要開銷大,特別是上述代碼中在迭代中訪問。因此多次訪問全局對象時,盡可能使用局部變量指向全局對象。如此,只需要一次遍歷整個作用域鏈,后面的復雜度為O(1)。總之,將需要多次訪問的全局對象儲存在局部變量中准沒錯。
復雜度
剛剛提到了復雜度,這里就補充算法的復雜度,並無深入研究,但絕對少不了。最快速的算法常數值表示為O(1),另外還有O(log n)、O(n)、O(n2)、O(n3),分別為對數、線性、平方、立方,依次復雜度越來越高,其中n表示值的數量。
常量值的獲取是復雜度為O(1),也即是說無論獲取多少個常量值,時間都是一樣的。
而對象的訪問獲取復雜度為O(n),也就是訪問時間隨數量值的增加而增加,成線性規律。相比而言,數組的訪問要快些,因為對象訪問必須在原型鏈中對該名稱的屬性進行一次遍歷。在同樣可以使用索引數字或者屬性名稱的對象中,使用索引訪問更快捷,當然優先使用屬性專用訪問方式(.)。
function updateUI(){ var i = divs.length, doc = document, divs = doc.getElementsByTagName("div"); for(; i ; i--){ divs[i-1].innerHTML = doc.title + " div " + (i - 1); } }
上訴代碼為性能犧牲了代碼易讀性,所以性能優化和代碼優雅以及易讀性等各方面需要綜合考慮,找出適合需求的方案才是最佳解決方案。
迭代優化
循環是性能優化中非常重要的地方,像其他編程語言一樣,javascript對於循環優化也有大量研究。例如:
- 減值迭代,大多數的情況下,我們都喜歡從0開始,然后自增進行迭代控制。而事實上,進行減值迭代,可以提高35%的性能(firefox測試結果)。也就是使用最大值,然后進行自減迭代控制;
- 簡化判斷條件,這中間有比較多的技巧,例如:利用變量自增或自減,當等於0時退出循環;while(i),就沒必要寫出while(i < 10)之類的情況;另外,就是NodeList對象的length屬性的訪問,在迭代中循環訪問屬性length也比較消耗資源,良好習慣是用移出循環,或者循環定義語句中儲存局部變量中;
- 簡化循環主體,這是重點,一定要確保最大限度地優化,確保沒有任何可以被移出循環的計算或者變量定義等。計算或者沒必要的語句,大部分開發人員都懂得移出,但是經常會看見變量的定義出現在循環體中,除非必要情況,否則也要移出到循環之外;
- 使用后判斷循環,for和while都是前判斷循環,do-while是后判斷循環,因為可以避免最初終止條件的運算,所以會相對快點。
var i = 10; do{ console.log(i); }while(i--);
其他需要注意事項:
- 使用原生方法,因為這些方法都是C/C++之類的編譯語言編寫,所以一般都要比javascript的快。
- 使用Switch語句,比使用一系列if-else語句要快出許多,另外,case語句可以按照最多可能到達的到最少可能到達的順序組織。
- 避免使用雙重解釋,例如:eval("alert('Hello')");setTimeout("alert('hello')", 500);因為初次解釋過程中,不能解釋字符串中的語句,因此需要另外實例化一個解釋器來進行解 釋,自然會降低性能。
- 謹慎地使用閉包,畢竟對內存資源消耗太大。
- 修改元素樣式,盡可能使用添加css class或者替換css class方式.
DOM交互的性能優化
DOM交互是javascript中最消耗資源的操作。一次小小的操作,都會導致頁面回流,瀏覽器需要重新計算無數尺寸進行相應的更新。所以,DOM交互性能優化的基本原則就是:盡可能減少DOM操作次數。
當遇上需要使用DOM對頁面進行更新時,推薦使用文檔碎片(documentFragment)來構建DOM結構,最后一次性或者少次添加到DOM文檔中去。當然,能使用innerHTML屬性時,義不容辭絕對優先使用innerHTML,這個地方千萬別客氣!因為,當使用innerHTML設置屬性值時,后台會自動創建一個HTML解釋器,然后使用內部的DOM方法創建DOM結構,而不是基於javascript的DOM方法。因為內部方法是編譯好的,因此執行比較快。
使用事件冒泡機制,任何可以冒泡的事件不僅可以在目標元素上處理,也可以在目標元素的祖先節點進行處理。因此,可以使用冒泡機制在更高層進行事件處理,如此可以負責多個目標事件的處理。
其他優化
至於,壓縮及其部署過程中的優化,可以參加yahoo的14條優化原則進行優化。寫的很不全面,因此,極力推薦精讀Steve Souders的兩本巨作,后面一本是合著。