Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is "this" more effective than a saved selector?

I was doing this test case to see how much using the this selector speeds up a process. While doing it, I decided to try out pre-saved element variables as well, assuming they would be even faster. Using an element variable saved before the test appears to be the slowest, quite to my confusion. I though only having to "find" the element once would immensely speed up the process. Why is this not the case?

Here are my tests from fastest to slowest, in case anyone can't load it:

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" );

I'd have assumed the order would go 4, 3, 1, 2 in order of which one has to use the selector to "find" the variable more often.

UPDATE: I have a theory, though I'd like someone to verify this if possible. I'm guessing that on click, it has to reference the variable, instead of just the element, which slows it down.

like image 343
James G. Avatar asked Mar 23 '14 20:03

James G.


People also ask

Which selector has the strongest selector specificity?

ID selectors have the highest specificity amongst the CSS selectors: 1, 0, 0, 0. Because of the unique nature of the ID attribute definition, we are usually styling a really specific element when we call up the ID in our CSS. – The specificity is 1, 0, 0, 1 because we have a type selector and an ID selector.

What makes a good selector?

The best and most simple selector will always be to use an element ID: #some-id-here . If only we were so lucky to have this every time. When writing selectors, you should prioritize finding in this order: ID, name, class, or anything else that is unique to the element.

What is the purpose of a selector?

A selector is one of the properties of the object that we use along with the component configuration. A selector is used to identify each component uniquely into the component tree, and it also defines how the current component is represented in the HTML DOM.

Which CSS selector is faster?

popupbutton is the fastest.


2 Answers

You don't really test the performance between the different techniques.

If you look at the output of the console for this modified test: http://jsperf.com/this-vs-thatjames/8

You will see how many event listeners are attached to the #bar object. And you will see that they are not removed at the beginning for each test.

So the following tests will always become slower as the previous ones because the trigger function has to call all the previous callbacks.

like image 45
t.niese Avatar answered Oct 16 '22 23:10

t.niese


Fixed test case: http://jsperf.com/this-vs-thatjames/10

TL;DR: Number of click handlers executed in each test grows because the element is not reset between tests.

The biggest problem with testing for micro-optimizations is that you have to be very very careful with what you're testing. There are many cases where the testing code interferes with what you're testing. Here is an example from Vyacheslav Egorov of a test that "proves" multiplication is almost instantaneous in JavaScript because the testing loop is removed entirely by the JavaScript compiler:

// 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);
  }
})

Since you're already aware there is something counter-intuitive going on, you should pay extra care.

First of all, you're not testing selectors there. Your testing code is doing: zero or more selectors, depending on the test, a function creation (which in some cases is a closure, others it is not), assignment as the click handler and triggering of the jQuery event system.

Also, the element you're testing on is changing between tests. It's obvious that the width in one test is more than the width in the test before. That isn't the biggest problem though. The problem is that the element in one test has X click handlers associated. The element in the next test has X+1 click handlers. So when you trigger the click handlers for the last test, you also trigger the click handlers associated in all the tests before, making it much slower than tests made earlier.

I fixed the jsPerf, but keep in mind that it still doesn't test just the selector performance. Still, the most important factor that skewes the results is eliminated.


Note: There are some slides and a video about doing good performance testing with jsPerf, focused on common pitfalls that you should avoid. Main ideas:

  • don't define functions in the tests, do it in the setup/preparation phase
  • keep the test code as simple as possible
  • compare things that do the same thing or be upfront about it
  • test what you intend to test, not the setup code
  • isolate the tests, reset the state after/before each test
  • no randomness. mock it if you need it
  • be aware of browser optimizations (dead code removal, etc)
like image 103
Tibos Avatar answered Oct 16 '22 23:10

Tibos