Learning JavaScript

This page lists some common misconceptions and solutions to improve JavaScript performance or proper code in general. The intention is to speed up our code base and to make sure code does what it intends. For example  returns true, for starters.

Selector performance (jQuery)
Just like the browser's stylesheet engine that parses native CSS, jQuery's selector engine (Sizzle) selects from right to left. Let's understand what this means for the following selector which may look fast / specific but in fact is not.

This particular example may be extreme, but consider the individual tips and tricks.

The above example will initiate the Sizzle engine, starting by getting all Anchor tags on the entire page (there's quite a few! Over 1300 on an average edit page on a Wikimedia wiki, don't forget about hidden links and anchors for javascript actions). After getting all those it will check these one by one to see if their parentNode is a ListItem. If it's not it'll get the parentNode of the parentNode and so on, until it's all the way to the document.body and gives up and get's started on the next anchor tag. The anchor tags in the Sidebar and in the Personal-area will match for a while for ListItem and UnorderedList and eventually they are dropped when it comes to the  (since the sidebar and personal-area are not descendants of #bodyContent). After that it'll still continue for the few anchor tags left in the match, eventhough there is only one #bodyContent it'll do one last check in the selector since  was part of the selector as well.

Sizzle has a number of optimization shortcuts for re-rooting queries in certain known forms, and on browsers that support querySelectorAll in many cases (such as this one) the entire query is handed off to native browser code bypassing the Sizzle engine entirely.

So how to make this more efficient ? The answer is. Context means to get elements from within the context of one or more elements, rather than from the entire. In plain javascript this is how that goes: So the example given is much faster like this due to context find:

You can optimize even further (depending on your scenario) like this:
 * Dropped the  selector.
 * Removed  from the selector. Unless you expect to have invalid ListItems without a list around them or if you specifically need to filter out OrderedLists, there's no need to specify each element in the tree.
 * Added a direct-child expression to speed up the filtering of anchor tags. Ofcourse if your situation may contain  then you don't want this. But whenever you can, use the direct child expression.

Generally one could describe writing more effecient selectors as "Be specific in what you want, and do not baby the selector engine". Specify what you really want, don't add in more crap if that's not what you need (ie. stuff that just happens to be the case like the extra "div" in front of the ID and the "ul" in between. Unless, as noted, if you specifically need the "div" or "ul" in which case it's fine to add them in the selector).

Null or undefined
There are more than a dozen ways of comparing or looking at a variable to determine whether it is null or undefined (or either of them). Look at the following for actual performance numbers and decide for yourself what gives the best performance for your scenario:
 * http://jsperf.com/testing-undefined
 * http://jsperf.com/testing-null
 * http://jsperf.com/testing-null-or-undefined

The fastest way to check if an (un)passed argument or object property is undefined is by strictly comparing it to the  literal.
 * Argument or object property undefined

So why is a typeof comparison slow ? The reason is that there are a few things going on. For one, the typeof operator returns a string which is a fairly expensive operation. So what really happens is an operation followed by a string comparison. Check the jsperf's above to compare results.
 * What about typeof ?

So why do people use ? It's often used to avoid throwing a ReferenceError if the variable was undeclared. However in pretty much every scenario the variable isn't undeclared, just undefined. The two can be confusing, but once you know it will make your code cleaner, simpler and faster.
 * Variable undeclared

Arguments and object properties are never considered undeclared. The only scenario in which referring to a variable throws an exception is when the variable is undeclared (in addition to being undefined). And the only way that can happen is by referring to a (local or implied global) variable. Which should be rare in most cases, and in good quality code not happen at all. See the following scenarios

Comparing to  is no different from any other comparison and results in the same:

iAmUndeclared was either intended to be a local variable not declared through, or a global variable that for some reason isn't available. Either way a bad situation that can be prevented.


 * Local variable

There is one more common scenario I'd like to touch here and that's variable declared/defined conditionally. This principle is a bad habbit. Local variables are under your direct control, if those are undeclared something went horribly wrong! Consider the following construction: One has to use  because   could be undeclared, since the   is inside the condition. So would this really be a situation in which we need  ? No.

This is generally a bad way to define a variable. You can declare (without defining) the variable ahead of time: This also makes the code a lot cleaner and shorter. It also makes it's scope clear beyond a doubt. JsHint and JsLint occasionally spit out warnings as well when a variable is not declared before it is defined within an if-statement (for good reasons, as shown above).

Lastly, to put these cases in a real-world scenario, let's provide default values / fallbacks for variables, in a way that doesn't need a typeof check either. Or even, if your argument won't / shouldn't be false-y:
 * Fallback

Verifying a variable to be either 'null' or 'undefined' (like  in PHP) can be done by a loose  comparison to null, which returns true for null as well as for undefined.
 * Null OR undefined

Array detection
Arrays do not have a  "array". Nor do they validate as "instanceof Array" (atleast not consistently and cross-browser).

As of JavaScript 1.8.5, the array object constructor has an native object method (not a prototype) called "isArray". It can be used like this:

However this is new and not cross-browser yet. The other reliable way to do this to call " " and verify that it is equal to " ". However this is slightly slower.

jQuery has had a built-in object method called "isArray" that does the latter isString comparison that works cross-browser. As of jQuery 1.4 (up to atleast 1.6.1) it only does that if the, faster, native Array.isArray isn't available. The best of both.

So the safest and fastest way to check if a variable is an array is to use.

Build part of an OR RegExp

 * http://jsperf.com/build-or-regex-array-vs-string

WikiEditor
Some stuff Catrope discovered when optimizing EditToolbar. These may not be universally true, so when in doubt run both alternatives through the Firebug profiler or use http://jsperf.com


 * Building simple HTML that doesn't need stuff like .data or .click yourself and feeding it to .html or .append is literally about 100 times as fast (YMMV) as building it with jQuery
 * Even if you do need .data or .click, something like this is still faster (at least for 20-200 children, YMMV):
 * Avoid setting .data on hundreds of elements that have a common parent or near ancestor. Instead, add an empty object to the parent's .data and fill that through a reference using the child's rel property as keys. This reduces the number of .data calls a lot.
 * .text is faster than .html if you're only adding text
 * .addClass('foo') is faster than .attr('class', 'foo')
 * but in complex cases involving more attributes and multiple classes, .attr({ ... }); is faster

jQuery.size vs .length attribute
As one can see in the jQuery source, the size function returns the  property of the jQuery object.

As there's no functional difference nor length in number of characters (" " vs. " ") and only a small performance penalty, its use is discouraged.