Javascript 中最常見的效能瓶頸以及最佳實務

來源:互聯網
上載者:User

轉自http://www.ibm.com/developerworks/cn/opensource/os-cn-dojodoh/

有了定位效能瓶頸的工具之後,開發也要具備辨認常見效能瓶頸的能力,以下是 Javascript 中常見的會導致效能下降的問題,以及對這些問題的最佳實務。
函數中使用過多的外部變數
範圍是理解 Javascript 運行機制的關鍵。Javascript 中的函數與我們熟知的 Java 或是 C++ 語言不同,Javascript 函數內部除了可以獲得傳入的參數以外,還可以訪問由 Javascript 引擎存取的 scope 屬性。簡單來說,scope 就是函數的範圍鏈,包含了所有該函數可以訪問的外部變數。

清單 3. 外部變數
        
 function render(){ 
   var body = document.body, 
     links = document.getElementsByTagName("a"); 
   document.getElementById("button").onclick = function(){ 
     handleClick(); 
   } 
  …
 } 

代碼中出現了三次對 document 的引用,由於 document 是存在於 scope 鏈頂端的全域對象,所以每次都會遍曆整個 scope 鏈,對效能影響很大。
最佳實務:
使用局部變數代替外部變數,以減少 javascript 解譯器查詢 scope 鏈次數。清單 4 中,用局部變數 doc 代替全域變數 document,減少了遍曆 scope 的次數。

清單 4. 局部變數 doc 代替全域變數 document
        
 function render(){ 
   var doc = document, 
     body = doc.body, 
     links = doc.getElementsByTagName("a"); 
   doc.getElementById("button").onclick = function(){ 
     handleClick(); 
   } 
   ... 
 } 

頻繁出現“.”操作符
點操作符會引起對象的逐層遍曆,對象成員嵌套越深,訪問該對象的速度就越慢。比如 A 對象下有包含 B 對象的 10 個成員,而 B 對象下有包含 C 對象的 20 個成員,那麼在最差的情況下,需要查詢 200 次才能訪問 A.B.C,這種查詢的效能消耗也是很可觀的。
最佳實務:
使用局部變數代替多級對象,以減少“.”操作次數,即降低物件查詢深度
直接操作 NodeList 對象
NodeList 是包含的 DOM 節點引用的類數組對象,包含 length 屬性。NodeList 對象一直處於即時更新的狀態,每當訪問對象屬性(如 length)的時候,都會觸發對應 HTML 物件查詢操作。下面是一個經典的“Live“ NodeList 的例子:

清單 5. 經典的“Live“ NodeList 的例子
        
 var links = document.getElementsByTagName("a"); 
 for(var i = 0; i < links.length;i++){ 
   var link = document.createElement("a") 
   document.body.appendChild(link); 
 } 

以上代碼原意是將頁面上連結數翻倍,但是每當頁面添加了一個 <a> 節點時,links.length 就自增 1,於是上溯代碼成為死迴圈。
最佳實務:
將 NodeList 轉化為數組後再進行操作。切斷節點引用與底層節點的即時聯絡,從而大大提高遍曆效能。
全域尋找 DOM 節點
由於 HTML 本身查詢節點操作存在效能限制,所以應該儘可能的縮小節點查詢範圍的廣度和深度。
最佳實務:
指定尋找的根節點,縮小遍曆 DOM 樹的大小。
直接對 DOM 樹進行修改
對 DOM 樹的每次可見的修改都會引起 DOM 樹的重排,而重排是代價非常昂貴的操作。會導致介面的拖慢,應盡量減少這類操作。
最佳實務:
使用 DocumentFragment,進行 DOM 的修改。在除去基於 WebKit(Chrome 和 Safari)以外的瀏覽器上,innerHTML 比標準的 DOM 方法(如 createElement 等)更快些;而在以 WebKit 為核心的瀏覽器上,兩者相差無幾。所以最佳的局部頁面修改方案應該是:合成代表 HTML 片段的字串,然後將其賦給對應的 innerHTML。
頻繁修改布局相關樣式
修改布局相關的樣式也會導致頁面的重排,如下面的代碼造成了 3 次頁面重排:

清單 6. 造成頁面的重排
        
 vary element = document.getElementById("div1"); 
 element.style.borderLeft = '1px'; 
 element.style.borderRight = '2px'; 
 element.style.padding = '5px'; 

最佳實務:
合并修改布局的樣式,減少瀏覽器 reflow 次數。

清單 7. 合并修改布局的樣式
        
 var element = dojo.byId("div1"); 
 element.style.cssText += "; border-left:1px; border-right: 2px; padding: 5px;"; 

Javascript 代碼已耗用時間超出瀏覽器最大限制
由於大多數瀏覽器使用單個線程執行 Javascript 和更新使用者介面,這意味著 Javascript 處於運行狀態時,使用者介面處於“鎖定”狀態。所以瀏覽器限制了 Javascript 的最長連續已耗用時間,確保瀏覽器不會被惡意代碼或錯誤迴圈伺服器用戶端檔案鎖。當到達瀏覽器限制時,使用者可以看到下面的對話方塊。

圖 8. 指令碼逾時警告
圖 8. 指令碼逾時警告 
最佳實務
對於一些需要長時間啟動並執行 Javascript 任務,使用定時器隔斷該任務,使 UI 線程有機會更新介面。

清單 8. 更新介面
        
 setTimeout(function(){ 
   // deferred javascript 1 
   setTimeout(function(){ 
     // deferred javascript2 
     setTimeout(function(){ 
       // deferred javascript2 
     }, 100) 
   }, 100) 
 }, 100) 

清單 8 中的定時器代碼簡明易懂,但維護性較差,下面是一個更易使用的遞迴版本定時器:

清單 9. 定時器代碼
        
 function timer(argArray, process, context){ 
   setTimeout(function(){ 
     var item = argArray.shift(); 
     process.call(context, item); 
     if(array.length > 0){ 
       setTimeout(arguments.callee, 100); 
     } 
   }, 100) 
 } 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.