如何提升JavaScript的運行速度之函數篇(二)

來源:互聯網
上載者:User

這篇是Nicholas討論如果防止指令碼失控的第二篇,主要討論了如何重構嵌套迴圈、遞迴,以及那些在函數內部同時執行很多子操作的函數。基本的思
想和上一節trunk()那個例子一致,如果幾個操作沒有特定的執行順序,而且互相不是依賴關係,我們就可以通過非同步呼叫的方式加以執行,不止可以減少執
行的次數,還可以防止指令碼失控。本文還介紹了通過memoization技術取代遞迴的方法。

【原文標題】Speed up your JavaScript, Part 2

【原文作者】Nicholas C. Zakas

以下是對原文的翻譯:

上周我在《too much happening in a loop
》(譯文:如何提升JavaScript的運行速度(迴圈篇)
)這篇文章中介紹了JavaScript已耗用時間過長的第一個原因。相似的情況有時也出現在函數的定義上,函數也可能因為使用不當而過載使用。通常情況是函數內包含了過多的迴圈(不是在迴圈中執行了過多的內容),太多的遞迴,或者只不過是太多不相干但又要一起執行的操作。


多的迴圈經常是以嵌套的形式出現,這種代碼會一直佔用JavaScript引擎直至迴圈結束。這方面有一個非常著名的例子,就是使用冒泡演算法排序。由於
JavaScript有內建的sort()方法,我們沒有必要使用這種方式進行排序,但我們可以藉助這個演算法理解嵌套迴圈佔用資源的癥結所在,從而避免類
似情況的發生。下面是一個在JavaScript使用冒泡排序法的典型例子:

function bubbleSort(items) {for (var i = items.length - 1; i >= 0; i--) {   for (var j = i; j >= 0; j--) {       if (items[j] < items[j - 1]) {           var temp = items[j];           items[j] = items[j - 1];           items[j - 1] = temp;       }   }}}

回憶一下你在學校學習的電腦知識,你可能記得冒泡排序法是效率最低的排序演算法之一,原因是對於一個包含n個元素的數組,必須要進行n的平方次的迴圈操
作。如果數組中的元素數非常大,那麼這個操作會持續很長時間。內迴圈的操作很簡單,只是負責比較和交換數值,導致問題的最大原因在於迴圈執行的次數。這會
導致瀏覽器運行異常,潛在的直接結果就是那個指令碼失控的警告對話方塊。

幾年前,Yahoo的研究員Julien Lecomte寫了一篇題為《Running CPU Intensive JavaScript Computations in a Web Browser
》的文章,在這篇文章中作者闡述了如何將很大的javaScript操作分解成若干小部分。其中一個例子就是將冒泡排序法分解成多個步驟,每個步驟只遍曆一次數組。我對他的代碼做了改進,但方法的思路還是一樣的:

function bubbleSort(array, onComplete) {var pos = 0; (function() {   var j, value;   for (j = array.length; j > pos; j--) {       if (array[j] < array[j - 1]) {           value = data[j];           data[j] = data[j - 1];           data[j - 1] = value;       }   }   pos++;   if (pos < array.length) {       setTimeout(arguments.callee, 10);   } else {       onComplete();   }})();}

這個函數藉助一個非同步管理器來實現了冒泡演算法,在每次遍曆數組以前暫停一下。onComplete()函數會在數組排序完成後觸發,提示使用者資料已經準備
好。bubbleSort()函數使用了和chunk()函數一樣的基本技術(參考我的上一篇文章),將行為封裝在一個匿名函數中,將
arguments.callee傳遞給setTimeout()以達到重複操作的目的,直至排序完成。如果你要將嵌套的迴圈拆解成若干個小步驟,以達到
解放瀏覽器的目的,這個函數提供了不錯的指導意見。

相似的問題還包括過多的遞迴。每個額外的遞迴調用都會佔用更多的記憶體,從而減慢瀏覽器的運行。惱人的是,你可能在瀏覽器發出指令碼失控警告之前,就耗盡了系統的記憶體,導致瀏覽器處於停止回應的狀態。Crockford在部落格上曾經對這個問題
進行過深入的討論。他當時使用的例子,就是用遞迴產生一個斐波那契數列。

function fibonacci(n) {return n < 2 ? n: fibonacci(n - 1) + fibonacci(n - 2);};

按照Crockford的說法,執行fibonacci(40)這條語句將重複調用自身331160280次。避免使用遞迴的方案之一就是使用memoization
技術,這項技術可以擷取上一次調用的執行結果。Crockford介紹了下面這個函數,可以為處理數值的函數增加這項功能:

function memoizer(memo, fundamental) {var shell = function (n) {   var result = memo[n];   if (typeof result !== 'number') {       result = fundamental(shell, n);       memo[n] = result;   }   return result;};return shell;};

他接下來將這個函數應用在斐波那契數列產生器上:

var fibonacci = memoizer([0, 1],function(recur, n) {  return recur(n - 1) + recur(n - 2);});

這時如果我們再次調用fibonacci(40),只會重複調用40次,和原來相比提高得非常多。memoization的原理,概括起來就一句話,同樣的結果,你沒有必要計算兩次。如果一個結果你可能會再次使用,把這個結果儲存起來,總比重新計算一次來的快。

最後一個可能讓函數執行緩慢的原因,就是我們之前提到過的,函數裡面執行了太多的內容,通常是因為使用了類似下面的開發模式:

function doAlot() {  doSomething();  doSomethingElse();  doOneMoreThing();}

在這裡要執行三個不同的函數,請注意,無論是哪個函數,在執行過程中都不依賴其他的函數,他們在本質是相對獨立的,只是需要在一個特定時間逐一執行而已。同樣,你可以使用類似chunk()的方法來執行一系列函數,而不會導致鎖定瀏覽器。

function schedule(functions, context) {setTimeout(function() {   var process = functions.shift();   process.call(context);   if (functions.length > 0) {       setTimeout(arguments.callee, 100);   }},100);}

schedule函數有兩個參數,一個是包含要執行函數的數組,另外一個是標明this所屬的內容物件。函數數組以隊列方式實現,Timer事件每次觸發的時候,都會將隊列最前面的函數取出並執行,這個函數可以通過下面的方式執行一系列函數:

schedule([doSomething, doSomethingElse, doOneMoreThing], window);

很希望各個JavaScript的類庫都增加類似這樣的進程處理函數。YUI在3.0時就已經引入了Queue
對象,可以通過timer連續調用一組函數。


論現有的技術可以協助我們將複雜的進程拆分到什麼程度,對於開發人員來說,使用這種方法來理解並確定指令碼失控的瓶頸是非常重要的。無論是太多的迴圈、遞迴還
是其他的什麼,你現在應該知道如果處理類似的情況。但要記住,這裡提到的技術和函數只是起到拋磚引玉的作用,在實際的應用中,你應該對它們加以改進,這樣
才能發揮更大的作用。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.