編寫高效能的JavaScript

我是你的超級英雄發表於2019-02-23

隨著計算機硬體的不斷升級,開發者越發覺得Javascript效能優化的好不好對網頁的執行效率影響不明顯,所以一些效能方面的知識被很多開發者忽視。但在某些情況下,不優化的Javascript程式碼必然會影響使用者的體驗。因此,即使在當前硬體效能已經大大提升的時代,在編寫Javascript程式碼時,若能遵循Javascript規範和注意一些效能方面的知識,對於提升程式碼的可維護性和優化效能將大有好處。那麼,接下來我們討論幾種能夠提高JavaScript效能的方法。

1、js檔案載入和執行

(1)將<script>標籤放到<body>標籤的底部
(2)可以合併多個js檔案,減少頁面中<script>標籤改善效能
(3)使用 defer 屬性,載入後續文件元素的過程將和 script.js 的載入並行進行,但是 script.js 的執行要在所有元素解析完成之後,DOMContentLoaded 事件觸發之前完成。
(4)使用 async 屬性,載入和渲染後續文件元素的過程將和 script.js 的載入與執行並行進行
(5)動態載入指令碼元素,無論在何時啟動瞎子,檔案的下載和執行過程都不會阻塞頁面其它程式

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'file.js';
document.getElementsByTagName('head')[0].appendChild(script);複製程式碼

2、識別符號所在的作用域鏈的位置越深

識別符號所在的作用域鏈的位置越深,那麼它的識別符號解析的效能就越慢。所以一個好的效能提升的經驗法則是:如果某個跨作用域的值在函式中被引用一次以上,那麼就把它儲存到區域性變數裡。

function fun1() {  
// 將全域性變數的引用先儲存在一個區域性變數中,然後使用這個區域性變數代替全域性變數,從而提高         
// 效能;不然每次(3次)都要遍歷整個作用域鏈找到
document  var doc = document;   
 var bd = doc.body;  
 var links = doc.getElementsByTagName('a');  
 doc.getElementById('btn').onclick = function(){   
 console.log('btn');  
 }
}複製程式碼

3、避免過長原型鏈繼承

方法或屬性在原型鏈中存在的位置越深,搜尋它的效能也就越慢,所以要避免N多層原型鏈的寫法。

4、物件成員巢狀過深

物件的巢狀成員,物件成員巢狀越深,讀取速度也就越慢。所以好的經驗法則是:如果在函式中需要多次讀取一個物件屬性,最佳做法是將該屬性值儲存在區域性變數中,避免多次查詢帶來的效能開銷。

function f() { 
 // 因為在以下函式中需要3次用到DOM物件屬性,所以先將它儲存在一個區域性變數        
 // 中,然後使用這個區域性變數代替它進行後續操作,從而提高效能  
var dom = YaHOO.util.Dom;  
if(Dom.hasClass(element,'selected')){   
  Dom.removeClass(elemet,'selected');  
}else{   
  Dom.addClass(elemet,'selected');  
 }
}複製程式碼

5、DOM操作

js訪問和操作DOM都會帶來效能損失,可通過以下幾點來減少效能損失:

(1)儘可能減少DOM訪問次數;
(2)如果需要多次訪問某個DOM節點,請使用區域性變數儲存它的引用;
(3)小心處理HTML集合,因為它實時連繫著底層文件;我們可以把集合的長度快取到一個變數中,並在迭代中使用它;
(4)下述情況會發生重排:
  • 新增或刪除可見的DOM元素;
  • 元素位置改變;
  • 元素尺寸改變(包括:外邊距、內邊距、邊框厚度、寬度、高度等屬性);
  • 內容改變(例如:文字改變或圖片被另一個不同尺寸的圖片改變);
  • 頁面渲染器初始化;
  • 瀏覽器視窗尺寸改變
可通過以下方式減少重排:
  • 留意上面會導致重排的操作,儘量避免;
  • 獲取佈局資訊的操作會導致強制渲染佇列重排,應該儘量避免使用以下獲取佈局資訊的操作方法或屬性或者快取佈局資訊,例如:offsetTop,offsetLeft,offsetWidthoffsetHeight,scrollTop,scrollLeft,scrollWidth,scrollHeight,clientTop,clientLeft,clientWidth,clientHeight,getComputedStyle()等;
  • 批量修改樣式,例如使用:

function f() {  
  // 推薦使用以下操作  
  var el1 = document.getElementById('mydiv');  
  el1.style.cssText = 'border:1px;padding:2px;margin:3px';  
  // 不推薦使用以下操作  
  var el2 = document.getElementById('mydiv');  
  el2.style.border = '1px';  
  el2.style.padding = '2px';  
  el2.style.margin = '3px';
}複製程式碼
  • 當需要批量修改DOM時,可以通過以下步驟減少重繪和重排的次數:
    • 使元素脫離文件流(隱藏元素、拷貝元素)
    • 對其應用多重改變;
    • 把元素帶回文件中
  • 使用事件委託(事件逐層冒泡並能被父級元素捕獲,使用事件代理,只需給外層元素繫結一個處理器,就可以處理其子元素上觸發的所用事件),因為給DOM元素繫結事件以及瀏覽器需要跟蹤每個事件處理器都需要消耗效能。

6、字串連線

str += 'one'+'two';
str= str+'one'+'two';複製程式碼
後者方式會比前者少在記憶體中建立一個臨時字串,所以效能有相應的提升,所以,所以推薦後者的寫法。

7、直接使用字面量

建立物件和陣列推薦使用字面量,因為這不僅是效能最優也有助於節省程式碼量。

var obj = {   
 name:'tom',    
 age:15,    
 sex:'男'
}複製程式碼

8、陣列長度快取

如果需要遍歷陣列,應該先快取陣列長度,將陣列長度放入區域性變數中,避免多次查詢陣列長度。

9、迴圈比較

JS提供了三種迴圈:for(;;)、while()、for(in)。在這三種迴圈中 for(in)的效率最差,因為它需要查詢Hash鍵,因此應儘量少用for(in)迴圈,for(;;)、while()迴圈的效能基本持平。

10、少用eval

儘量少使用eval,每次使用eval需要消耗大量時間,這時候使用JS所支援的閉包可以實現函式模板。

11、字串轉換

當需要將數字轉換成字元時,採用如下方式:"" + 1。從效能上來看,將數字轉換成字元時,有如下公式:("" +) > String() > .toString() > new String()String()屬於內部函式,所以速度很快。而.toString()要查詢原型中的函式,所以速度遜色一些,new String()需要重新建立一個字串物件,速度最慢。

12、浮點數轉換整形

當需要將浮點數轉換成整型時,應該使用Math.floor()或者Math.round()。而不是使用parseInt(),該方法用於將字串轉換成數字。而且Math是內部物件,所以Math.floor()其實並沒有多少查詢方法和呼叫時間,速度是最快的。

以上就是總結的12條 JavaScript高效能程式設計的一些建議,希望對你有借鑑之處,如果對你有啟發,請點贊鼓勵,如果有疑問或建議,歡迎留言討論。


相關文章