標籤:
1) 控製作用域
對變數的符號的查詢總是沿著範圍鏈, 從局部範圍開始查詢, 如果查到了就停止, 否則繼續查詢外層範圍 ....一直到全域範圍為止。
所以: 對全域變數的訪問時間 > 對局部變數的訪問時間。因此要局部變數緩衝全域對象可以提高效能。
// Bad example:// 對於全域對象 document的訪問, 每次訪問document都會進行範圍鏈尋找, 一直尋找到全域範圍, 降低了效能.function updateUI_Bad() { var imgs = document.getElementsByTagName("img"); for (var i = 0, len = imgs.length; i < len; i++) { imgs[i].title = document.title + " image " + i; } var msg = document.getElementById("msg"); msg.innerHTML = "Update complete.";}
// Good example:// 把全域對象document存在本地. 除了第一次, 以後使用doc, 減少了範圍鏈的訪問時間.function udpateUI_Good() { var doc = document; var imgs = doc.getElementsByTagName("img"); for (var i = 0, len = imgs.length; i < len; i++) { imgs[i].title = doc.title + " image " + i; } var msg = doc.getElementById("msg"); msg.innerHTML = "Update complete."; }
2) 避免不必要的屬性尋找
一旦多次用到的對象屬性, 應該將其儲存在局部變數中。第一次訪問該值複雜度是O(n), 以後訪問的複雜度是O(1).
不要console(a.b.c); console(a.b.c); console(a.b.c); console(a.b.c); console(a.b.c);
而要 var c = a.b.c; console(c); console(c); console(c); console(c); console(c);
// 訪問變數, 數位複雜度是 O(1)var value = 5; // O(1)var sum = 10 + value; // O(1)
// 訪問數組中的元素的複雜度也是O(1)var values = [5, 10]; // O(1)var sum = values[0] + values[1];// O(1)
// 訪問對象上的屬性的複雜度是 O(n)
// Bad example:var query = window.location.href.substring(window.location.href.indexOf("?"));// 尋找 "substring" (window.location.href.substring) 有3次尋找// 尋找 "indexOf" (window.location.href.indexOf) 有3次尋找
// Good example:var url = window.location.href;var query = url.substring(url.indexOf("?"));// 只有4次屬性尋找
3) 避免雙重解釋
如果程式碼封裝含在字串中, javascript 代碼啟動並執行同時必須啟動一個新的解析器來解析新的代碼(寫成字串形式的代碼)。而啟動一個新的解析器會產生開銷, 影響效率。
// Badeval("alert(‘Hello world!‘)");// Goodalert(‘Hello world!‘);
// Badvar sayHi = new Function("alert(‘Hello world!‘)");// Goodvar sayHi = function() { alert(‘Hello world!‘);};
// BadsetTimeout("alert(‘Hello world!‘)", 5000);// GoodsetTimeout(function() { alert(‘Hello world!‘);}, 5000)
4) 減少 DOM 更新次數
JS 的 DOM 操作開銷非常大 : 每次個更改(插入 or 刪除), 瀏覽器都要重新計算各種元素的size, 從而需要重新渲染頁面的一部分甚至整個頁面, 所以DOM更新次數越多, 開銷越大。
// Bad example// 添加每個item 需要 2 次 DOM 更新 : 一個添加 <li> 元素, 兩一個給 <li> 添加文本節點.// 為 list 添加 10 個元素, 共需要 20 次 DOM 更新.var list = document.getElementById("myList");for (var i = 0; i < 10; i++) { var item = document.createElement("li"); list.appendChild(item); item.appendChild(document.createTextNode("Item " + i));}
// Good example// 使用 DocumentFragment, 把 <li> 添加到 DocumentFragment 上, 最後一次性地把 DocumentFragment 更新到 <list> 上var list = document.getElementById("myList"),fragment = document.createDocumentFragment();for (var i = 0; i < 10; i++) { var item = document.createElement("li"); item.appendChild(document.createTextNode("Item " + i)); fragment.appendChild(item);}list.appendChild(fragment);
5) innerHTML
innerHTML 速度 > createElement() + appendChild()。
因為 後台會為 innerHTML 建立一個 HTML 解析器, 使用內部的 DOM 調用建立 DOM 結構, 而不是基於 JS 的 DOM 調用。內部方法是編譯好的二進位調用而不是解析調用, 所以速度快。
使用 innerHTML 時需要注意最小化對 innerHTML 的調用:
// Bad examplefor (var i = 0; i < 10) { list.innerHTML += "<li>Item " + i + "</li>"; // 這裡每一次運算, 都會產生一次 DOM 操作: // 1) innerHTML += "<li>Item "; 2) innerHTML += i; 3) innerHTML += "</li>";}
// Good examplefor (var i = 0; i < 10) { var item = "<li>Item " + i + "</li>"; list.innerHTML += item;}
6) 注意 NodeList
操作返回 NodeList 對象的操作包括: getElementsByTagName(), 擷取元素的 childNodes 屬性, 擷取元素的 attributes 屬性, 訪問特殊集合: document.form / document.images。
訪問 DOCUMENT 的 元素 list 開銷很大, 應該盡量減少對 元素 list的訪問。
//var imageList = document.getElementsByTagName("img"); // imageList : NodeList// Good examplefor (var i = 0, len = imageList.length; i < len; i++) { // 使用 len 儲存 imageList.length, 而不是每次都訪問 imageList.length. var image = imageList[i]; // 使用 image 保持當前的 imageList[i], 而不是每次都訪問 imageList[i].}
javascript 的效能問題