首頁  >  文章  >  web前端  >  如何提升JavaScript Web效能的技巧總結

如何提升JavaScript Web效能的技巧總結

伊谢尔伦
伊谢尔伦原創
2017-07-20 10:22:031091瀏覽

Javascript 效能最佳化絕不是一種書面的技術,Nicholas 的技術演進列出了10個建議,幫助你寫出高效的JS 程式碼

1. 定義局部變數

當一個變數被引用的時候,JavaScript會在作用域鏈中的不同成員中尋找這個變數。作用域鏈指的是目前作用於下可用變數的集合,它在各種主流瀏覽器中至少包含兩個部分:局部變數的集合和全域變數的集合。

簡單來說,如果JavaScript引擎在作用域鏈中搜尋的深度越大,那麼操作就會消耗更多的時間。引擎先從 this 開始尋找局部變量,然後是函數參數、本地定義的變量,最後遍歷所有的全域變數。

因為局部變數在這條鏈的起端,所以尋找局部變數總是比尋找全域變數要塊。所以當你想要不只一次地使用一個全域變數的時候,你應該將它定義成局部變量,就像這樣:


#
var blah = document.getElementById('myID'),
blah2 = document.getElementById('myID2');

改寫成


var doc = document,
blah = doc.getElementById('myID'),
blah2 = doc.getElementById('myID2');

2. 不要使用with() 語句

這是因為with() 語句將會在作用域鏈的開始加入額外的變數。額外的變數意味著,當任何變數需要被存取的時候,JavaScript引擎都需要先掃描with()語句產生的變量,然後才是局部變量,最後是全域變數。
So with() essentially gives local variables all the performance drawbacks of global ones, and in turn derails Javascript optimization. 因此with()語句同時給局部變數和全域變數的效能帶來負面影響,最終使我們優化JavaScriptScript性能的計劃破產。

3. 小心使用閉包

#雖然你可能還不知道“閉包”,但你可能在不經意間經常使用這項技術。閉包基本上被認為是JavaScript中的new,當我們定義一個即時函數的時候,我們就使用了閉包,例如:

 document.getElementById('foo').onclick = function(ev) { };

閉包的問題在於:根據定義,在它們的作用域鏈中至少有三個物件:閉包變數、局部變數和全域變數。這些額外的物件將會導致第1和第2個建議中提到的效能問題。

但我認為Nicholas並不是要我們因噎廢食,閉包對於提高程式碼可讀性等方面還是非常有用的,只是不要濫用它們(尤其在循環中)。

4. 物件屬性和陣列元素的速度都比變數慢

談到JavaScript的數據,一般來說有4種存取方式:數值、變數、物件屬性和數組元素。在考慮最佳化時,數值和變數的效能差不多,且速度顯著優於物件屬性和陣列元素。
因此當你多次引用一個物件屬性或陣列元素的時候,你可以透過定義一個變數來獲得效能提升。 (這一條在讀取、寫入資料時都有效)
雖然這條規則在絕大多數情況下是正確的,但是Firefox在優化數組索引上做了一些有意思的工作,能夠讓它的實際性能優於變數。但考慮到數組元素在其他瀏覽器上的效能弊端,還是應該盡量避免數組查找,除非你真的只針對於火狐瀏覽器的效能而進行開發。

5. 不要在陣列中挖得太深

另外,程式設計師應該避免在陣列中挖得太深,因為進入的層數越多,操作速度就越慢。
簡單地說,在嵌套很多層的數組中操作很慢是因為數組元素的查找速度很慢。試想如果操作嵌套三層的陣列元素,就要執行三次組元素查找,而不是一次。
因此如果你不斷地引用 foo.bar, 你可以透過定義 var bar = foo.bar 來提高效能。

6. 避免 for-in 迴圈(和基於函數的迭代)

這是另一個非常教條的建議:不要使用for-in迴圈。
這背後的邏輯非常直接:要遍歷一個集合內的元素,你可以使用諸如for循環、或者do-while循環來替代for-in循環,for-in循環不僅僅可能需要遍歷額外的數組項,還需要更多的時間。
為了遍歷這些元素,JavaScript需要為每個元素建立一個函數,而這個基於函數的迭代帶來了一系列效能問題:額外的函數引入了函數物件被建立和銷毀的上下文,將會在作用域鏈的頂端增加額外的元素。

7. 在迴圈時將控制條件和控制變數合併起來

提到效能,在迴圈中需要避免的工作一直是個熱門話題,因為迴圈會被重複執行很多次。所以如果有效能優化的需求,先對循環開刀有可能會得到最明顯的效能提升。
一種最佳化迴圈的方法是在定義迴圈的時候,將控制條件和控制變數合併起來,以下是一個沒有將他們合併起來的例子:

 for ( var x = 0; x < 10; x++ ) {
};

當我們要添加什麼東西到這個循環之前,我們發現有幾個操作在每次迭代都會出現。 JavaScript引擎需要:

#1:检查 x 是否存在
#2:检查 x 是否小于 0 (译者注:我猜这里是作者的笔误)
#3:使 x 增加 1

然而如果你只是迭代元素中的一些元素,那么你可以使用while循环进行轮转来替代上面这种操作:


var x = 9;
do { } while( x-- );

如果你想更深入地了解循环的性能,Zakas提供了一种高级的循环优化技巧,使用异步进行循环(碉堡了!)

8. 为HTML集合对象定义数组

JavaScript使用了大量的HTML集合对象,比如 document.forms,document.images 等等。通常他们被诸如 getElementsByTagName、getElementByClassName 等方法调用。

由于大量的DOM selection操作,HTML集合对象相当的慢,而且还会带来很多额外的问题。正如DOM标准中所定义的那样:“HTML集合是一个虚拟存在,意味着当底层文档被改变时,它们将自动更新。”这太可怕了!

尽管集合对象看起来跟数组很像,他们在某些地方却区别很大,比如对于特定查询的结果。当对象被访问进行读写时,查询需要重新执行来更新所有与对象相关的组分,比如 length。

HTML集合对象也非常的慢,Nicholas说好像在看球的时候对一个小动作进行60倍速慢放。另外,集合对象也有可能造成死循环,比如下面的例子:


var ps = document.getElementsByTagName(&#39;p&#39;);
 
for (var i=0; i < ps.length; i++ ) {
  var p = document.createElement("p"); 
  document.appendChild(p);
}

这段代码造成了死循环,因为 ps 表示一个实时的HTML集合,并不是你所期望的数组。这种实时的集合在添加 e388a4556c0f65e1904146cc1a846bee 标签时被更新,所以i < p.length 永远都不会结束。

解决这个问题的方法是将这些元素定义成数组,相比只设置 var ps = document.getElementsByTagName(‘p') 稍微有点麻烦,下面是Zakas提供的强制使用数组的代码:


function array(items) {
  try {
    return Array.prototype.concat.call(items);
  } catch (ex) {
 
    var i    = 0,
      len   = items.length,
      result = Array(len);
 
    while (i < len) {
      result[i] = items[i];
      i++;
    }
 
    return result;
  }
}
 
var ps = array( document.getElementsByTagName(&#39;p&#39;) );
 
for (var i=0l i < ps.length; i++ ) {
  var p = document.createElement("p"); 
  document.appendChild(p);
}

9. 不要碰DOM!

不使用DOM是JavaScript优化中另一个很大的话题。经典的例子是添加一系列的列表项:如果你把每个列表项分别加到DOM中,肯定会比一次性加入所有列表项到DOM中要慢。这是因为DOM操作开销很大。
Zakas对这个进行了细致的讲解,解释了由于回流(reflow)的存在,DOM操作是非常消耗资源的。回流通常被理解为浏览器重新选渲染DOM树的处理过程。比如说,如果你用JavaScript语句改变了一个p的宽度,浏览器需要重绘页面来适应变化。
任何时候只要有元素被添加到DOM树或者从DOM树移除,都会引发回流。使用一个非常方便的JavaScript对象可以解决这个问题——documentFragment,我并没有使用过,但是在Steve Souders也表示同意这种做法之后我感觉更加肯定了。
DocumentFragment 基本上是一种浏览器以非可视方式实现的类似文档的片段,非可视化的表现形式带来了很多优点,最主要的是你可以在 documentFragment 中添加任何结点而不会引起浏览器回流。

10. 修改CSS类,而不是样式

你也许听说过:修改CSS类必直接修改样式会更高效。这归结于回流带来的另一个问题:当布局样式发生改变时,会引发回流。
布局样式意味着任何影响改变布局的变化都会强制引起浏览器回流。比如宽度、高度、字号、浮动等。
但是别误会我的意思,CSS类并不会避免回流,但是可以将它的影响最小化。相比每次修改样式都会引起回流,使用CSS类一次修改多个样式,只需要承担一次回流带来的消耗。
因此在修改多个布局样式的时候,使用CSS类来优化性能是明智的选择。另外如果你需要在运行时定义很多歌CSS类,在DOM上添加样式结点也是不错的选择。

以上是如何提升JavaScript Web效能的技巧總結的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn