根據Nicholas的說法,有四種代碼會拖慢腳本的運行,並最終致使腳本失控。分別是次數過多的同步循環、龐大的函數體、不恰當的遞歸和不合理的DOM調用。這篇着重講第一個緣由。最後給出了一個開發模式,替換傳統的循環結構,能夠徹底避免腳本失控的情況發生。javascript
【原文標題】Speed up your JavaScript, Part 1
【原文做者】Nicholas C. Zakas
譯文出自:http://cuimingda.com/2009/01/speed-up-your-javascript-part-1.html
在我上一篇帖子(譯文:瀏覽器根據什麼來斷定腳本失控?) 中,談到了各個瀏覽器究竟會在什麼狀況下彈出腳本失控提示,對於Internet Explorer來講,當瀏覽器執行了數量過多的語句時就會中止執行腳本,而其餘的瀏覽器,則是持續執行腳本超過必定時間的時候就會給出提示。而咱們要探 討的核心問題,不是這些瀏覽器若是探測失控的腳本,而是咱們如何纔可讓腳本運行的更快一些,從而避免這些警告。
腳本失控基本上有如下四個方面的緣由:
1. 在循環中執行了太多的操做。
2. 臃腫的函數體
3. 過多的遞歸
4. 過多的DOM調用
在 這篇帖子中,我將會把重點放到第一條上:循環中的過多操做。循環的操做是同步進行的,因此執行一個循環所花費的時間徹底取決於循環的次數。所以有兩種狀況 會致使循環執行的時間過長,並直接致使鎖定瀏覽器。一是循環體中包含了太多的操做,二是循環的次數過多。這兩種狀況都能直接致使鎖定瀏覽器,並顯示腳本失 控的提示。
解決這個問題的訣竅就是用下面這兩個問題來評估每一個循環:
1. 這個循環必需要同步執行麼?
2. 循環裏面的數據,必需要按順序執行麼?
若是兩個問題的答案都是否認的話,你就能夠選擇將循環裏的操做進行分解。關鍵是要根據代碼的具體環境肯定上面兩個問題的答案。一個典型的循環可能像下面這個樣子:
html
for(var i=0; i < items.length; i++){
process(items[i]);
}
乍一看這個循環並無太大的問題,是否是會運行很長時間徹底取決於循環的次數。若是緊接循環後沒有其餘代碼在執行的時候須要依賴於循環的結果,那麼對於第 一個問題的答案就是「不」。你還能夠發現,循環每次只處理一個數值,並且不依賴於上一次循環的結果,因此對於第二個問題的答案一樣也是否認的。這就意味 着,循環能夠經過某種方式進行拆解,不會致使鎖定瀏覽器而顯示腳本失控的提示。
在《Professional JavaScript, Second Edition》這本書中,對於那些執行次數很是巨大的虛幻,我推薦使用下面的方式來處理:
java
function chunk(array, process, context){
setTimeout(function(){
var item = array.shift();
process.call(context, item);
if (array.length > 0){
setTimeout(arguments.callee, 100);
}
}, 100);
}
chunk()函數的用途就是將一個數組分紅小塊處理(這也是名字的由來),咱們能夠傳遞三個參數。要處理的數組對象、處理函數以及一個可選的上下文變 量,用於設置process()函數中對應的this對象。第一個timer用於處理操做之間的延時(這裏設置爲100毫秒,你們能夠根據實際須要自行修 改)。每次執行這個函數,都會將數組中的第一個對象取出,並傳給process()函數進行操做,若是這時process()中還有未處理完的對象,另外 一個timer就會啓動,用於重複等待。上面提到的循環,能夠經過下面的方法使用這個函數:
web
chunk(items, process);
須要注意的是,在這裏數組採用了隊列(queue)的形式,並且在循環的過程當中,每次都會發生修改。若是你要修改數組的原始狀態,這裏介紹兩種途徑:一種是經過concat()函數,在傳遞以前,創建一個當前數組的副本:
數組
chunk(items.concat(), process);
另一種選擇是直接修改chunk()函數,直接在函數內部進行修改:
瀏覽器
function chunk(array, process, context){
var items = array.concat(); //clone the array
setTimeout(function(){
var item = items.shift();
process.call(context, item);
if (items.length > 0){
setTimeout(arguments.callee, 100);
}
}, 100);
}
注意這種方法要比只保存一個索引安全的多,由於數組的內容在下次計時器生效以前可能會發生變化。
這裏提到的chunk()函數,只是優化 循環性能的一個起點。你能夠根據須要不斷改進它,讓它擁有更多的功能。好比說,在數組中全部對象都處理完成之後,能夠增長一個函數回調。不管你是否會按照 這種方式對函數進行修改,這只是一種JavaScript的代碼開發模式,能夠幫助優化數組的處理性能,還能夠避免那個腳本失控的警告。安全