讀書筆記(03) - 性能 - JavaScript高級程序設計

coding

做用域鏈查找

做用域鏈的查找是逐層向上查找。查找的層次越多,速度越慢。隨着硬件性能的提高和瀏覽器引擎的優化,這個慢咱們基本能夠忽略。html

除了層級查找損耗的問題,變量的修改應只在局部環境進行,儘可能避免在局部環境下去操做修改父級變量的值。(react/vue 單向數據流的數據傳輸方式)前端

優化方法:聲明一個變量存儲引用(該方法應用甚多)vue

沒必要要的屬性查找

// 未優化(window.location.href 3*2 6次)
var query = window.location.href.substring(window.location.href.indexOf('?'));

// 優化後(3次,之後屢次調用url,查詢次數不會增長)
var url = window.location.href;
var query = url.substring(url.indexOf('?'));
url = null;

window.location.href

函數裏面聲明的變量,在函數調用棧執行後退出時,會自動清除引用。而全局變量和閉包則會與之相反,繼續保存,因此使用用後需手動標記清除,以避免形成內存泄漏。react

優化循環

  1. 減值迭代
  2. 簡化終止條件
  3. 簡化循環體
  4. 使用後測試循環

減值迭代

平常應用很少,與增值迭代的區別,就在i存儲的值。減值迭代i的值不斷在變小,存儲的空間也在變小。jquery

但在前端極少須要遍歷上萬次上億次的數據,上千上百都不多,因此這個優化可忽略。並且咱們遍歷的順序通常都是從數組頭部開始,因此增值迭代應用的更多。數組

// 增值迭代(用的較多)
for(var i = 0; i < len; i++) {
    //...
}

// 減值迭代
for(var i = len - 1; i >= 0 ; i--) {
    //...
}

簡化終止條件 (經常使用)

終止條件應該是一個固定值判斷,應避免在終止條件上作其餘運算(屬性查找等)。瀏覽器

// 未優化,每次循環都會去計算數組長度
var arr = ['HTML', 'CSS', 'JavaScript'];
for (var i = 0; i < arr.length; i++) {
    //...
}

// 優化後
for (var i = 0, len = arr.length; i < len; i++) {
    //...
}
// 聲明瞭一個變量len用於儲存數組長度,只會計算一次

簡化循環體

循環體的代碼應該着重於只須要遍歷處理的代碼,其餘無關代碼應放置到循環體外面。閉包

後測試循環

最經常使用的for循環和while循環都是前測試循環。而do-while這種後測試循環,能夠避免最初終止條件的計算,所以運行更快。

前測試循環(for/while),可能一次都不會執行循環體app

後測試循環(do...while),至少執行一次less

用肯定索引值更快

// for循環遍歷
var arr = ['HTML', 'CSS', 'JavaScript'];
for (let i = 0, len = arr.length; i < len; i++) {
    arr[i];
}

// 肯定索引值
arr[0]; 
arr[1]; 
arr[2];

其餘

  1. 原生方法較快(Math)
  2. switch語句較快 (多個if狀況下)
  3. 位運算符較快

TIPS: 判斷優化,最可能的到最不可能的順序組織(if/switch)

最小語句數

符合 write less, do more 的代碼追求

多個變量聲明合併

// 多個var聲明
var name = 'KenTsang';
var age = 28;
var job = 'Developer';

// 合併一個var聲明
var name = 'KenTsang',
    age = 27,
    job = 'Developer';

插入迭代值

// 優化前
var name = value[i];
i++;

// 優化後
var name = value[i++];

數組/對象字面量

建立引用類型可使用構造函數和字面量兩種方式,不過平常習慣都使用字面量,由於語句更簡潔,寫起來更像數據封裝。

// 字面量
var arr = [1, 2, 3, 4];
var obj = {
    name: 'KenTsang'
}

// 構造函數
var arr = new Array(1, 2, 3, 4);
var obj = new Object();
obj.name = 'KenTsang';

DOM優化交互

最小現場更新

現場更新:一旦你須要訪問的 DOM 部分是已經顯示的頁面的一部分,那麼你就是在進行一個現場更新

文檔片斷

文檔片斷至關一個臨時的佔位符,只有片斷中的內容會被添加到DOM上,片斷自己並不會被添加。

// 代碼片斷標籤
var ele  = document.getElementById('ul');
var fragment = document.createDocumentFragment();
var browsers = ['Firefox', 'Chrome', 'Opera', 
    'Safari', 'IE'];

browsers.forEach(function(browser) {
    var li = document.createElement('li');
    li.textContent = browser;
    fragment.appendChild(li);
});

// 只會操做一次DOM
ele.appendChild(fragment);

innerHTML

合併插入代碼一次性設置innerHTML。

// 優化前:操做屢次DOM
var list = document.getElementById("myList");
for (var i=0; i < 10; i++) {
    list.innerHTML += "<li>Item " + i + "</li>";
}

// 優化後:操做一次DOM
var innerHtml = '';
for (var i = 0; i < 10; i++) {
    innerHtml += '<li>Item' + i + '</li>';
}
list.innerHTML = innerHtml;

事件代理(事件委託)

經過事件流——冒泡機制實現代理,子元素事件觸發冒泡到父元素,由父元素綁定一個事件進行統一處理,避免多個事件綁定影響性能。

事件冒泡

<ul class="list">
    <li class="item">HTML</li>
    <li class="item">CSS</li>
    <li class="item">JavaScript</li>
</ul>

var listEle = document.getElementById('list');

listEle.addEventListener('click', function(event) {
    if (event.target.className.indexOf('item') > -1) {
        console.log(event.target.innerHTML); 
    }
})

// jquery
$('#list').on('click', '.item', function(event){
    console.log($(this).html());
})

注意HTMLCollection

任什麼時候候要訪問 HTMLCollection,無論它是一個屬性仍是一個方法,都是在文檔上進行一個查詢,這個查詢開銷很昂貴。
// 一個死循環例子
<a href="">link</a>
    
var existLinkEle = document.getElementsByTagName('a');
for (var i = 0; i < existLinkEle.length; i++) {     
    console.log(i);
    var linkEle = document.createElement('a');
    document.body.appendChild(linkEle);
}
// body會不斷地插入a標籤

由於existLinkEle.length每次循環都會從新計算頁面a節點的數量,而獲得的值一直遞增。

// 優化(一個變量存儲引用)
var len = existLinkEle.length;
for (var i = 0; i < len; i++) {
    //...
}

返回HTMLCollection對象狀況有:

  1. document.getElementByTagName()
  2. 獲取元素的childNodes屬性
  3. 獲取元素的attributes屬性
  4. document.forms,document.images

參考文檔

做者:以樂之名 本文原創,有不當的地方歡迎指出。轉載請指明出處。
相關文章
相關標籤/搜索