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