前言
隨著計算機硬件的不斷升級,開發(fā)者越發(fā)覺得JAVAScript性能優(yōu)化的好不好對網(wǎng)頁的執(zhí)行效率影響不明顯,所以一些性能方面的知識被很多開發(fā)者忽視。但在某些情況下,不優(yōu)化的JavaScript代碼必然會影響用戶的體驗。因此,即使在當(dāng)前硬件性能已經(jīng)大大提升的時代,在編寫Javascript代碼時,若能遵循Javascript規(guī)范和注意一些性能方面的知識,對于提升代碼的可維護(hù)性和優(yōu)化性能將大有好處。那么,接下來我們討論幾種能夠提高JavaScript性能的方法。
1、js文件加載和執(zhí)行
(1)將<script>標(biāo)簽放到<body>標(biāo)簽的底部
(2)可以合并多個js文件,減少頁面中<script>標(biāo)簽改善性能
(3)使用 defer 屬性,加載后續(xù)文檔元素的過程將和script.js的加載并行進(jìn)行,但是 script.js的執(zhí)行要在所有元素解析完成之后,DOMContentLoaded 事件觸發(fā)之前完成。
(4)使用 async 屬性,加載和渲染后續(xù)文檔元素的過程將和script.js的加載與執(zhí)行并行進(jìn)行
(5)動態(tài)加載腳本元素,無論在何時啟動瞎子,文件的下載和執(zhí)行過程都不會阻塞頁面其它進(jìn)程
var script = document.createElement('script'); script.type = 'text/javascript'; script.src = 'file.js'; document.getElementsByTagName('head')[0].AppendChild(script);
2、標(biāo)識符所在的作用域鏈的位置越深
標(biāo)識符所在的作用域鏈的位置越深,那么它的標(biāo)識符解析的性能就越慢。所以一個好的性能提升的經(jīng)驗法則是:如果某個跨作用域的值在函數(shù)中被引用一次以上,那么就把它存儲到局部變量里。
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、避免過長原型鏈繼承
方法或?qū)傩栽谠玩溨写嬖诘奈恢迷缴睿阉魉男阅芤簿驮铰砸苊釴多層原型鏈的寫法。
4、對象成員嵌套過深
對象的嵌套成員,對象成員嵌套越深,讀取速度也就越慢。所以好的經(jīng)驗法則是:如果在函數(shù)中需要多次讀取一個對象屬性,最佳做法是將該屬性值保存在局部變量中,避免多次查找?guī)淼男阅荛_銷。
function f() { // 因為在以下函數(shù)中需要3次用到DOM對象屬性,所以先將它存儲在一個局部變量 // 中,然后使用這個局部變量代替它進(jìn)行后續(xù)操作,從而提高性能 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訪問次數(shù);
(2)如果需要多次訪問某個DOM節(jié)點,請使用局部變量存儲它的引用;
(3)小心處理html集合,因為它實時連系著底層文檔;我們可以把集合的長度緩存到一個變量中,并在迭代中使用它;
(4)下述情況會發(fā)生重排:
- 添加或刪除可見的DOM元素,請參閱JavaScript系統(tǒng)學(xué)習(xí)DOM系列文章之理解DOM節(jié)點關(guān)系;
- 元素位置改變;
- 元素尺寸改變(包括:外邊距、內(nèi)邊距、邊框厚度、寬度、高度等屬性);
- 內(nèi)容改變(例如:文本改變或圖片被另一個不同尺寸的圖片改變);
- 頁面渲染器初始化;
- 瀏覽器窗口尺寸改變
可通過以下方式減少重排:
- 留意上面會導(dǎo)致重排的操作,盡量避免;
- 獲取布局信息的操作會導(dǎo)致強制渲染隊列重排,應(yīng)該盡量避免使用以下獲取布局信息的操作方法或?qū)傩曰蛘呔彺娌季中畔ⅲ纾簅ffsetTop,offsetLeft,offsetWidthoffsetHeight,``scrollTop,scrollLeft,scrollWidth,scrollHeight,clientTop,clientLeft,clientWidth,clientHeight,getComputedStyle()等;
- 批量修改樣式,推薦修改class來實現(xiàn),例如使用:
function f() { // 推薦使用以下操作 var el1 = document.getElementById('mydiv'); el1.className = 'mydiv'; // 不推薦使用以下操作 var el2 = document.getElementById('mydiv'); el2.style.border = '1px'; el2.style.padding = '2px'; el2.style.margin = '3px'; } .mydiv { border: 1px; padding: 2px; margin: 3px; }
- 當(dāng)需要批量修改DOM時,可以通過以下步驟減少重繪和重排的次數(shù):
- 使元素脫離文檔流(隱藏元素、拷貝元素、DocumentFragment 請參考JavaScript系統(tǒng)學(xué)習(xí)DOM系列文章之DocumentFragment)
- 對其應(yīng)用多重改變;
- 把元素帶回文檔中
- 使用事件委托(事件逐層冒泡并能被父級元素捕獲,使用事件代理,只需給外層元素綁定一個處理器,就可以處理其子元素上觸發(fā)的所用事件),因為給DOM元素綁定事件以及瀏覽器需要跟蹤每個事件處理器都需要消耗性能。
6、字符串連接
str += 'one'+'two'; str= str+'one'+'two';
后者方式會比前者少在內(nèi)存中創(chuàng)建一個臨時字符串,所以性能有相應(yīng)的提升,所以,所以推薦后者的寫法。
7、直接使用字面量
創(chuàng)建對象和數(shù)組推薦使用字面量,因為這不僅是性能最優(yōu)也有助于節(jié)省代碼量。
var obj = { name:'tom', age:15, sex:'男' }
?
8、數(shù)組長度緩存
如果需要遍歷數(shù)組,應(yīng)該先緩存數(shù)組長度,將數(shù)組長度放入局部變量中,避免多次查詢數(shù)組長度。
for(let i = 0, len = arr.lenght; i < len; i++) { ... }
9、循環(huán)比較
JS提供了三種循環(huán):for(;;)、while()、for(in)。在這三種循環(huán)中 for(in)的效率最差,因為它需要查詢Hash鍵,因此應(yīng)盡量少用for(in)循環(huán),for(;;)、while()循環(huán)的性能基本持平。
10、少用eval
盡量少使用eval,每次使用eval需要消耗大量時間,這時候使用JS所支持的閉包可以實現(xiàn)函數(shù)模板。
11、字符串轉(zhuǎn)換
當(dāng)需要將數(shù)字轉(zhuǎn)換成字符時,采用如下方式:"" + 1。從性能上來看,將數(shù)字轉(zhuǎn)換成字符時,有如下公式:("" +) > String() > .toString() > new String()。String()屬于內(nèi)部函數(shù),所以速度很快。而.toString()要查詢原型中的函數(shù),所以速度遜色一些,new String()需要重新創(chuàng)建一個字符串對象,速度最慢。
12、浮點數(shù)轉(zhuǎn)換整形
當(dāng)需要將浮點數(shù)轉(zhuǎn)換成整型時,應(yīng)該使用Math.floor()或者M(jìn)ath.round()。而不是使用parseInt(),該方法用于將字符串轉(zhuǎn)換成數(shù)字。而且Math是內(nèi)部對象,所以Math.floor()其實并沒有多少查詢方法和調(diào)用時間,速度是最快的。
后記
暫時只記錄了這么多,大家如果有好的建議,歡迎在評論區(qū)備注,我會將大家是建議補充進(jìn)文章中,方便我們在工作和面試中避免采坑
參考文章:
https://github.com/fengshi123/blog/issues/6