Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is checking for an attribute using dot notation before removing faster than removing the attribute outright?

I asked this question, and it turned out that when removing an attribute from an element, checking whether the element exists first using elem.xxx!==undefined makes the runtime faster. Proof.

Why is it quicker? There's more code to go through and you'll have to encounter the removeAttribute() method whichever way you go about this.

like image 708
dayuloli Avatar asked Mar 21 '14 17:03

dayuloli


People also ask

What is the difference between dot notation and bracket notation?

Dot notation is faster to write and easier to read than bracket notation. However, you can use variables with bracket notation, but not with dot notation. This is especially useful for situations when you want to access a property but don't know the name of the property ahead of time.

How does dot notation work in JavaScript?

Dot notation is one way to access a property of an object. To use dot notation, write the name of the object, followed by a dot (.), followed by the name of the property. Example: var cat = { name: 'Moo', age: 5, }; console.

Which object property access notation is slightly faster than other?

Dot notation is faster to write and clearer to read.

Under what circumstances would you need to use bracket notation to access a value in an object Select all that apply?

We must use bracket notation whenever we are accessing an object's property using a variable or when the property's key is a number or includes a symbol or is two words with a space.


2 Answers

Well, first thing you need to know is that elem.xxx is not the same as elem.getAttribute() or any other method relative to the attribute.

elem.xxx is a property of a DOM element while attribute and element on the HTML inside the DOM, both are similar but different. For exemple, take this DOM element: <a href="#"> and this code :

//Let say var a is the <a> tag
a.getAttribute('href');// == #
a.href;// == http://www.something.com/# (i.e the complet URL)

But let take a custom attribute : <a custom="test">

//Let say var a is the <a> tag
a.getAttribute('custom');// == test
a.custom;// == undefined

So you can't really compare the speed of both since they don't achieve the same result. But one is clearly faster since properties are a fast access data while attribute use the get/hasAttribute DOM functions.

Now, Why without the condition is faster? Simply because removeAttribute doesn't care is the attribute is missing, it check if it is not.

So using hasAttribute before removeAttribute is like doing the check twice, but the condition is a little slower since it need to check if the condition is satisfied to run the code.

like image 184
Karl-André Gagnon Avatar answered Sep 29 '22 14:09

Karl-André Gagnon


I have a suspicion that the reason for the speed boost are trace trees.

Trace trees were first introduced by Andreas Gal and Michael Franz of the University of California, Irvine, in their paper Incremental Dynamic Code Generation with Trace Trees.

In his blog post Tracing the Web Andreas Gal (the co-author of the paper) explains how tracing Just-in-Time compilers works.

To explain tracing JIT compilers as sententiously as possible (since my knowledge about the subject isn't profound) a tracing JIT compiler does the following:

  1. Initially all the code to be run is interpreted.
  2. A count is kept for the number of times each code path is executed (e.g. the number of times the true branch of an if statement is executed).
  3. When the number of times a code path is taken is greater than a predefined threshold the code path is compiled into machine code to speed up execution (e.g. I believe SpiderMonkey executes code paths executed more than once).

Now let's take a look at your code and understand what is causing the speed boost:

Test Case 1: Check

if (elem.hasAttribute("xxx")) {
    elem.removeAttribute("xxx");
}

This code has a code path (i.e. an ifstatement). Remember that tracing JITs only optimize code paths and not entire functions. This is what I believe is happening:

  1. Since the code is being benchmarked by JSPerf it's being executed more than once (an understatement). Hence it is compiled into machine code.
  2. However it still incurs the overhead of the extra function call to hasAttribute which is not JIT compiled because it's not a part of the conditional code path (the code between the curly braces).
  3. Hence although the code inside the curly braces is fast the conditional check itself is slow because it's not compiled. It is interpreted. The result is that the code is slow.

Test Case 2: Remove

elem.removeAttribute("xxx");

In this test case we don't have any conditional code paths. Hence the JIT compiler never kicks in. Thus the code is slow.

Test Case 3: Check (Dot Notation)

if (elem.xxx !== undefined) {
    elem.removeAttribute("xxx");
}

This is the same as the first test case with one significant difference:

  1. The conditional check is a simple non-equivalence check. Hence it doesn't incur the full overhead of a function call.
  2. Most JavaScript interpreters optimize simple equivalence checks like this by assuming a fixed data type for both the variables. Since the data type of elem.xxx or undefined is not changing every iteration this optimization makes the conditional check even faster.
  3. The result is that the conditional check (although interpreted) does not slow down the compiled code path significantly. Hence this code is the fastest.

Of course this is just speculation on my part. I don't know the internals of a JavaScript engine and I hence my answer is not canonical. However I opine that it is a good educated guess.

like image 22
Aadit M Shah Avatar answered Sep 29 '22 15:09

Aadit M Shah