龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > web编程 > Javascript编程 >

编写高性能JavaScript(译)(3)

时间:2014-08-14 12:18来源:网络整理 作者:网络 点击:
分享到:
最糟的内存泄漏地方之一是在循环中,或者在setTimeout()/ setInterval()中,但这是相当常见的。思考下面的例子: var myObj = { callMeMaybe: function () { var myRef = this

最糟的内存泄漏地方之一是在循环中,或者在setTimeout()/ setInterval()中,但这是相当常见的。思考下面的例子:

var myObj = {
 callMeMaybe: function () {
  var myRef = this;
  var val = setTimeout(function () { 
   console.log('Time is running out!'); 
   myRef.callMeMaybe();
  }, 1000);
 }
};

如果我们运行myObj.callMeMaybe();来启动定时器,可以看到控制台每秒打印出“Time is running out!”。如果接着运行myObj = null,定时器依旧处于激活状态。为了能够持续执行,闭包将myObj传递给setTimeout,这样myObj是无法被回收的。相反,它引用到myObj的因为它捕获了myRef。这跟我们为了保持引用将闭包传给其他的函数是一样的。

同样值得牢记的是,setTimeout/setInterval调用(如函数)中的引用,将需要执行和完成,才可以被垃圾收集。

当心性能陷阱

永远不要优化代码,直到你真正需要。现在经常可以看到一些基准测试,显示N比M在V8中更为优化,但是在模块代码或应用中测试一下会发现,这些优化真正的效果比你期望的要小的多。

speed-trap

做的过多还不如什么都不做. 图片来源: Tim Sheerman-Chase.

比如我们想要创建这样一个模块:

  • 需要一个本地的数据源包含数字ID
  • 绘制包含这些数据的表格
  • 添加事件处理程序,当用户点击的任何单元格时切换单元格的css class

这个问题有几个不同的因素,虽然也很容易解决。我们如何存储数据,如何高效地绘制表格并且append到DOM中,如何更优地处理表格事件?

面对这些问题最开始(天真)的做法是使用对象存储数据并放入数组中,使用jQuery遍历数据绘制表格并append到DOM中,最后使用事件绑定我们期望地点击行为。

注意:这不是你应该做的

var moduleA = function () {

 return {

  data: dataArrayObject,

  init: function () {
   this.addTable();
   this.addEvents();
  },

  addTable: function () {

   for (var i = 0; i < rows; i++) {
    $tr = $('<tr></tr>');
    for (var j = 0; j < this.data.length; j++) {
     $tr.append('<td>' + this.data[j]['id'] + '</td>');
    }
    $tr.appendTo($tbody);
   }

  },
  addEvents: function () {
   $('table td').on('click', function () {
    $(this).toggleClass('active');
   });
  }

 };
}();

这段代码简单有效地完成了任务。

但在这种情况下,我们遍历的数据只是本应该简单地存放在数组中的数字型属性ID。有趣的是,直接使用DocumentFragment和本地DOM方法比使用jQuery(以这种方式)来生成表格是更优的选择,当然,事件代理比单独绑定每个td具有更高的性能。

要注意虽然jQuery在内部使用DocumentFragment,但是在我们的例子中,代码在循环内调用append并且这些调用涉及到一些其他的小知识,因此在这里起到的优化作用不大。希望这不会是一个痛点,但请务必进行基准测试,以确保自己代码ok。

对于我们的例子,上述的做法带来了(期望的)性能提升。事件代理对简单的绑定是一种改进,可选的DocumentFragment也起到了助推作用。

var moduleD = function () {

 return {

  data: dataArray,

  init: function () {
   this.addTable();
   this.addEvents();
  },
  addTable: function () {
   var td, tr;
   var frag = document.createDocumentFragment();
   var frag2 = document.createDocumentFragment();

   for (var i = 0; i < rows; i++) {
    tr = document.createElement('tr');
    for (var j = 0; j < this.data.length; j++) {
     td = document.createElement('td');
     td.appendChild(document.createTextNode(this.data[j]));

     frag2.appendChild(td);
    }
    tr.appendChild(frag2);
    frag.appendChild(tr);
   }
   tbody.appendChild(frag);
  },
  addEvents: function () {
   $('table').on('click', 'td', function () {
    $(this).toggleClass('active');
   });
  }

 };

}();
精彩图集

赞助商链接