为什么"这个"比保存的选择器更有效?

Jam*_*uld 18 javascript jquery

我正在做这个测试用例,看看使用this选择器加速进程的程度.在这样做时,我决定尝试预先保存的元素变量,假设它们会更快.使用在测试之前保存的元素变量似乎是最慢的,这让我很困惑.我虽然只需要"找到"该元素一次就会极大地加速这个过程.为什么不是这样?

以下是我从最快到最慢的测试,以防任何人无法加载它:

1

$("#bar").click(function(){
    $(this).width($(this).width()+100);
});
$("#bar").trigger( "click" );
Run Code Online (Sandbox Code Playgroud)

2

$("#bar").click(function(){
    $("#bar").width($("#bar").width()+100);
});
$("#bar").trigger( "click" );
Run Code Online (Sandbox Code Playgroud)

3

var bar = $("#bar");
bar.click(function(){
    bar.width(bar.width()+100);
});
bar.trigger( "click" );
Run Code Online (Sandbox Code Playgroud)

4

par.click(function(){
    par.width(par.width()+100);
});
par.trigger( "click" );
Run Code Online (Sandbox Code Playgroud)

我假设订单将按顺序进行4,3,1,2,其中必须使用选择器更频繁地"找到"变量.

更新:我有一个理论,但我希望有人在可能的情况下验证这一点.我猜测在点击时,它必须引用变量,而不仅仅是元素,这会减慢它的速度.

Tib*_*bos 9

修复了测试用例:http://jsperf.com/this-vs-thatjames/10

TL; DR:每个测试中执行的单击处理程序数量增加,因为元素未在测试之间重置.

测试微优化的最大问题是你必须非常小心你正在测试的东西.在许多情况下,测试代码会干扰您正在测试的内容.以下是来自Vyacheslav Egorov测试的一个例子,"证明"乘法在JavaScript中几乎是瞬时的,因为测试循环完全由JavaScript编译器删除:

// I am using Benchmark.js API as if I would run it in the d8.
Benchmark.prototype.setup = function() {
  function multiply(x,y) {
    return x*y;
  }
};

var suite = new Benchmark.Suite;
suite.add('multiply', function() {
  var a = Math.round(Math.random()*100),
      b = Math.round(Math.random()*100);

  for(var i = 0; i < 10000; i++) {
     multiply(a,b);
  }
})
Run Code Online (Sandbox Code Playgroud)

既然你已经意识到有一些违反直觉的事情,你应该特别小心.

首先,你不是在那里测试选择器.您的测试代码正在执行:零或更多选择器,具体取决于测试,函数创建(在某些情况下是闭包,其他情况不是),作为单击处理程序的赋值和jQuery事件系统的触发.

此外,您正在测试的元素是在测试之间进行更改.很明显,一次测试中的宽度大于之前测试中的宽度.但这不是最大的问题.问题是一个测试中的元素与X点击处理程序相关联.下一个测试中的元素有X + 1个点击处理程序.因此,当您触发上次测试的单击处理程序时,您还会触发之前所有测试中关联的单击处理程序,使其比之前的测试慢得多.

我修复了jsPerf,但请记住它仍然不测试选择器性能.尽管如此,消除了扭曲结果的最重要因素.


注意:有一些幻灯片和一个关于使用jsPerf进行良好性能测试的视频,重点关注您应该避免的常见陷阱.主要观点:

  • 不要在测试中定义函数,在设置/准备阶段执行
  • 保持测试代码尽可能简单
  • 比较做同样事情或提前做事的事情
  • 测试你想要测试的内容,而不是设置代码
  • 隔离测试,在每次测试之后/之前重置状态
  • 没有随机性.如果你需要它,嘲笑它
  • 注意浏览器优化(死代码删除等)


t.n*_*ese 6

您并没有真正测试不同技术之间的性能.

如果您查看此修改测试的控制台输出:http: //jsperf.com/this-vs-thatjames/8

您将看到有多少事件侦听器附加到该#bar对象.而且你会发现每次测试都没有删除它们.

因此,以下测试将始终变得比以前的测试慢,因为触发器函数必须调用所有先前的回调.