javascript 的性能问题

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 代码运行的同时必须启动一个新的解析器来解析新的代码(写成字符串形式的代码)。而启动一个新的解析器会产生开销, 影响效率。

// Bad
eval("alert(‘Hello world!‘)");
// Good
alert(‘Hello world!‘);
// Bad var sayHi = new Function("alert(‘Hello world!‘)"); // Good var sayHi = function() {   alert(‘Hello world!‘); };
// Bad setTimeout("alert(‘Hello world!‘)", 5000); // Good setTimeout(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 example
for (var i = 0; i < 10) {
  list.innerHTML += "<li>Item " + i + "</li>"; // 这里每一次运算, 都会产生一次 DOM 操作:
                            // 1) innerHTML += "<li>Item "; 2) innerHTML += i; 3) innerHTML += "</li>";
}
// Good example for (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 example
for (var i = 0, len = imageList.length; i < len; i++) {  // 使用 len 保存 imageList.length, 而不是每次都访问 imageList.length.
  var image = imageList[i];                 // 使用 image 保持当前的 imageList[i], 而不是每次都访问 imageList[i].
}

 

  

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。