Web性能優(yōu)化系列:10個(gè)提升JavaScript性能的技巧

1. 定義局部變量

當(dāng)一個(gè)變量被引用的時(shí)候,JavaScript將在作用域鏈中的不同成員中查找這個(gè)變量。作用域鏈指的是當(dāng)前作用于下可用變量的集合,它在各種主流瀏覽器中至少包含兩個(gè)部分:局部變量的集合和全局變量的集合。

簡單地說,如果JavaScript引擎在作用域鏈中搜索的深度越大,那么操作也就會(huì)消耗更多的時(shí)間。引擎首先從 this 開始查找局部變量,然后是函數(shù)參數(shù)、本地定義的變量,最后遍歷所有的全局變量。

因?yàn)榫植孔兞吭谶@條鏈的起端,所以查找局部變量總是比查找全局變量要塊。所以當(dāng)你想要不止一次地使用一個(gè)全局變量的時(shí)候,你應(yīng)該將它定義成局部變量。

2. 不要使用 with() 語句

這是因?yàn)?with() 語句將會(huì)在作用域鏈的開始添加額外的變量。額外的變量意味著,當(dāng)任何變量需要被訪問的時(shí)候,JavaScript引擎都需要先掃描with()語句產(chǎn)生的變量,然后才是局部變量,最后是全局變量。因此with()語句同時(shí)給局部變量和全局變量的性能帶來負(fù)面影響,最終使我們優(yōu)化JavaScript性能的計(jì)劃破產(chǎn)。

3. 小心使用閉包

閉包的問題在于:根據(jù)定義,在它們的作用域鏈中至少有三個(gè)對(duì)象:閉包變量、局部變量和全局變量。這些額外的對(duì)象將會(huì)導(dǎo)致第1和第2個(gè)建議中提到的性能問題。

但是閉包對(duì)于提高代碼可讀性等方面還是非常有用的,只是不要濫用它們(尤其在循環(huán)中)。

4. 對(duì)象屬性和數(shù)組元素的速度都比變量慢

談到JavaScript的數(shù)據(jù),一般來說有4種訪問方式:數(shù)值、變量、對(duì)象屬性和數(shù)組元素。在考慮優(yōu)化時(shí),數(shù)值和變量的性能差不多,并且速度顯著優(yōu)于對(duì)象屬性和數(shù)組元素。

因此當(dāng)你多次引用一個(gè)對(duì)象屬性或者數(shù)組元素的時(shí)候,你可以通過定義一個(gè)變量來獲得性能提升。(這一條在讀、寫數(shù)據(jù)時(shí)都有效)

5. 不要在數(shù)組中挖得太深

應(yīng)該避免在數(shù)組中挖得太深,因?yàn)檫M(jìn)入的層數(shù)越多,操作速度就越慢。

簡單地說,在嵌套很多層的數(shù)組中操作很慢是因?yàn)閿?shù)組元素的查找速度很慢。試想如果操作嵌套三層的數(shù)組元素,就要執(zhí)行三次數(shù)組元素查找,而不是一次。

6. 避免 for-in 循環(huán)(和基于函數(shù)的迭代)

這是另一條非常教條的建議:不要使用for-in循環(huán)。

這背后的邏輯非常直接:要遍歷一個(gè)集合內(nèi)的元素,你可以使用諸如for循環(huán)、或者do-while循環(huán)來替代for-in循環(huán),for-in循環(huán)不僅僅可能需要遍歷額外的數(shù)組項(xiàng),還需要更多的時(shí)間。

為了遍歷這些元素,JavaScript需要為每一個(gè)元素建立一個(gè)函數(shù),這種基于函數(shù)的迭代帶來了一系列性能問題:額外的函數(shù)引入了函數(shù)對(duì)象被創(chuàng)建和銷毀的上下文,將會(huì)在作用域鏈的頂端增加額外的元素。

7. 在循環(huán)時(shí)將控制條件和控制變量合并起來

提到性能,在循環(huán)中需要避免的工作一直是個(gè)熱門話題,因?yàn)檠h(huán)會(huì)被重復(fù)執(zhí)行很多次。所以如果有性能優(yōu)化的需求,先對(duì)循環(huán)開刀有可能會(huì)獲得最明顯的性能提升。

一種優(yōu)化循環(huán)的方法是在定義循環(huán)的時(shí)候,將控制條件和控制變量合并起來,下面是一個(gè)沒有將他們合并起來的例子:

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

8. 為HTML集合對(duì)象定義數(shù)組

JavaScript使用了大量的HTML集合對(duì)象,比如 document.forms,document.images 等等。通常他們被諸如 getElementsByTagName、getElementByClassName 等方法調(diào)用。
由于大量的DOM selection操作,HTML集合對(duì)象相當(dāng)?shù)穆?,而且還會(huì)帶來很多額外的問題。盡管集合對(duì)象看起來跟數(shù)組很像,他們?cè)谀承┑胤絽s區(qū)別很大,比如對(duì)于特定查詢的結(jié)果。當(dāng)對(duì)象被訪問進(jìn)行讀寫時(shí),查詢需要重新執(zhí)行來更新所有與對(duì)象相關(guān)的組分,比如 length。

9. 不要碰DOM!

不使用DOM是JavaScript優(yōu)化中另一個(gè)很大的話題。經(jīng)典的例子是添加一系列的列表項(xiàng):如果你把每個(gè)列表項(xiàng)分別加到DOM中,肯定會(huì)比一次性加入所有列表項(xiàng)到DOM中要慢。這是因?yàn)镈OM操作開銷很大。

由于回流(reflow)的存在,DOM操作是非常消耗資源的?;亓魍ǔ1焕斫鉃闉g覽器重新選渲染DOM樹的處理過程。比如說,如果你用JavaScript語句改變了一個(gè)div的寬度,瀏覽器需要重繪頁面來適應(yīng)變化。

任何時(shí)候只要有元素被添加到DOM樹或者從DOM樹移除,都會(huì)引發(fā)回流。使用一個(gè)非常方便的JavaScript對(duì)象可以解決這個(gè)問題——documentFragment。

DocumentFragment 基本上是一種瀏覽器以非可視方式實(shí)現(xiàn)的類似文檔的片段,非可視化的表現(xiàn)形式帶來了很多優(yōu)點(diǎn),最主要的是你可以在 documentFragment 中添加任何結(jié)點(diǎn)而不會(huì)引起瀏覽器回流。

10. 修改CSS類,而不是樣式

修改CSS類必直接修改樣式會(huì)更高效。這歸結(jié)于回流帶來的另一個(gè)問題:當(dāng)布局樣式發(fā)生改變時(shí),會(huì)引發(fā)回流。

布局樣式意味著任何影響改變布局的變化都會(huì)強(qiáng)制引起瀏覽器回流。比如寬度、高度、字號(hào)、浮動(dòng)等。

但是別誤會(huì)我的意思,CSS類并不會(huì)避免回流,但是可以將它的影響最小化。相比每次修改樣式都會(huì)引起回流,使用CSS類一次修改多個(gè)樣式,只需要承擔(dān)一次回流帶來的消耗。

因此在修改多個(gè)布局樣式的時(shí)候,使用CSS類來優(yōu)化性能是明智的選擇。另外如果你需要在運(yùn)行時(shí)定義很多個(gè)CSS類,在DOM上添加樣式結(jié)點(diǎn)也是不錯(cuò)的選擇。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容