我正在this test case查看使用this
选择器加快进程的速度。在这样做时,我决定尝试预先保存的元素变量,假设它们会更快。使用在测试之前保存的元素变量似乎是最慢的,这让我很困惑。我虽然只需要找到"该元素曾经极大地加速了这个过程。为什么不是这样?
以下是我从最快到最慢的测试,以防任何人无法加载它:
1
$("#bar").click(function(){
$(this).width($(this).width()+100);
});
$("#bar").trigger( "click" );
2
$("#bar").click(function(){
$("#bar").width($("#bar").width()+100);
});
$("#bar").trigger( "click" );
3
var bar = $("#bar");
bar.click(function(){
bar.width(bar.width()+100);
});
bar.trigger( "click" );
4
par.click(function(){
par.width(par.width()+100);
});
par.trigger( "click" );
我已经假设订单会按顺序排列为4,3,1,2,其中必须使用选择器"找到"变量更常见。
更新:我有theory,但我希望有人在可能的情况下验证这一点。我猜测在点击时,它必须引用变量,而不仅仅是元素,这会减慢它的速度。
答案 0 :(得分:9)
修正了测试用例:http://jsperf.com/this-vs-thatjames/10
TL; DR:每次测试中执行的点击处理程序数量增加,因为测试之间没有重置元素。
测试微优化的最大问题是你必须非常小心你正在测试的东西。在许多情况下,测试代码会干扰您正在测试的内容。这是来自Vyacheslav Egorov的一个example测试,“证明”乘法在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);
}
})
由于你已经意识到有一些违反直觉的行为,你应该特别小心。
首先,你没有在那里测试选择器。您的测试代码正在执行:零个或多个选择器,具体取决于测试,函数创建(在某些情况下是闭包,其他不是闭包),作为单击处理程序的赋值和jQuery事件系统的触发。
此外,您正在测试的元素正在测试之间进行更改。很明显,一次测试中的宽度大于之前测试中的宽度。但这不是最大的问题。问题是一个测试中的元素与X点击处理程序相关联。下一个测试中的元素有X + 1个点击处理程序。 因此,当您触发上次测试的单击处理程序时,您还会触发之前所有测试中关联的单击处理程序,使其比之前的测试慢得多。
我修复了jsPerf,但请记住它仍然不测试选择器性能。但是,消除结果偏差的最重要因素仍然存在。
注意:有一些slides和一个video关于使用jsPerf进行良好的性能测试,重点关注应该避免的常见陷阱。主要想法:
答案 1 :(得分:6)
你并没有真正测试不同技术之间的表现。
如果您查看此修改测试的控制台输出: http://jsperf.com/this-vs-thatjames/8
您将看到有多少事件侦听器附加到#bar
对象。
你会发现每次测试都不会删除它们。
因此,以下测试将始终变得比以前的测试慢,因为触发器函数必须调用所有先前的回调。
答案 2 :(得分:3)
这种速度增加的一些原因是因为对象引用已在内存中找到,因此编译器不必在内存中查找变量
$("#bar").click(function(){
$(this).width($(this).width()+100); // Only has to check the function call
}); // each time, not search the whole memory
而不是
var bar = $("#bar");
...
bar.click(function(){
bar.width(bar.width()+100); // Has to search the memory to find it
}); // each time it is used
正如zerkms所说,解除引用(必须按照上面的描述查找内存引用)对性能有some but little effect
因此,您执行的测试的差异缓慢的主要原因是DOM不会在每个函数调用之间重置。实际上,保存的选择器的执行速度与this
答案 3 :(得分:2)
看起来您获得的性能结果与代码无关。如果你看these edited tests,你可以看到在两个测试中(第一个和最后一个)使用相同的代码会产生完全不同的结果。
答案 4 :(得分:-4)
我不知道,但如果我不得不猜测我会说这是由于并发和多线程。
执行$(...)
时,调用jQuery构造函数并创建一个存储在内存中的新对象。但是,当您引用现有变量时,不会创建新对象(duh)。
虽然我没有引用来源,但我相信每个javascript事件都会在自己的线程中调用,因此事件不会互相干扰。通过这种逻辑,编译器必须锁定变量才能使用它,这可能需要时间。
再一次,我不确定。非常有趣的测试btw!