Notes on Paul Irish's "Things I learned from the jQuery source" casts

it2022-05-06  0

I watched these videos on 2012/12/14. Today's jQuery version is 1.8.3. So there might be some differences from the original video. I've briefly noted some of the differences between what described in the video and the current stable release. Most code he desribed can be found easily in speed/jquery-basic.js (1.4.2).

Episode 1

Anonymous Function


window object passed into anonymous function, for faster object accessing. 变成局部变量,为什么可以更快访问?也许是保存变量的地方不同。 c中,全局变量保存在全局/静态存储区。 而局部变量保存在栈中。 undefined as last parameter, to restore the potential overriden undefined object (e.g. undefined=true "a**hole effect").

(function(window, undefined) { // do stuff here })(window);

Asynchronous Recursion


setInterval may not work at the time you expected, when the action takes longer than the time you specified. 跟我理解的一样,主要就是在事件执行时间超过interval的时候会出问题

(function() { doStuff(); // gonna wait doStuff done then setTimeout(arguments.callee, 100); })();

arguments.callee deprecated in ES5 strict mode.

We can use named function:

(function loopback() { doStuff(); // gonna wait doStuff done then setTimeout(loopback, 100); })();



(didn't get it)



This is a bridge for class (DOM: className) and for (DOM: htmlFor), and some downcased-to-camelCase mappings (!)

jQuery.props.dt = "data-type"; $("#something").attr("dt"); // gets its data-type attribute



"Constant" variable to define animation speed (e.g. fast / slow). jQuery searches values in this object. If not found, it uses _default value.

jQuery.fx.speeds.kilo = 1024; // define a new speed with 1024ms duration $("#somethings").fadeIn("kilo");

DOM Ready callback


In jQuery 1.4.x, DOM Ready callback is implemented in jQuery.bindReady. However this function does not exist in 1.8.3 (current stable release). The most similar function is jQuery.ready.promise.

Implemenation details:

If DOMContentLoaded event is supported, use it. If document.attachEvent exists (old IEs), use it to listen to onreadystatechange event Otherwise, use doScrollCheck, keeps trying scrolling the document (1ms), until it does not raise exception, then it's ready. See also:

IEContentLoaded - An alternative for DOMContenloaded on Internet Explorer src/core.js#L828 (jQuery.ready.promise)

home-made getScript()


Outdated. He extracted $.getScript implementation of jQuery 1.4.1. Today's jQuery uses XMLHttpRequest.

The 1.4.x implementation can be found at speed/jquery-basic.js#L5060

Selector Performance


(may be outdated. See sizzle.js)

In 1.4.x,

$("#id").find("tag.thing") is faster than

$("#id tag.thing") because the latter must traverse the tree in JavaScript level instead of DOM level.

Todays' jQuery is Sizzle-driven and may already utilies this shortcut too.

:password is slower than input:password, because the former does traverse through all the possible tags.



jQuery use native parser (window.JSON.parse) if supported.

Otherwise, make a new function and invoke it:

(new Function("return "+ jsonSrc ))(); Note the capital F (not function): it makes a new function object, with source code in the parameter.

Since JSON is the literal representation of a JavaScript Object, the browser can parse the object literal inside the function, and turns it into a real object.

jQuery will check if the string is a real JSON string, otherwise it prints an error message to the console.

See also:

Function - JavaScript | MDN src/core.js#L490



It uses Sizzle.uniqueSort (same as today's implementation in 1.8.3).

The official document says that it does work for any array element other than DOM Element. It is still documented as "unspported" in today's 1.8.3.

Sorts an array of DOM elements, in place, with the duplicates removed. Note that this only works on arrays of DOM elements, not strings or numbers. However I've tried non-DOM element scalars in 1.8.3, and it does work for Number, String and true, but not work for null, false and undefined. Objects (non-scalar) with same properties are all kept, duplications are not removed. Though this behavior makes sense, because two individual objects are compared by object address, not its properties.


[tl;dr] $.unique doesn't guarantee correct result if there are items not DOM Element. So do use it for an array containing DOM elements only.

DOM elements:

div = document.createElement("div"); div.innerHTML = '<span class="a"></span><span class="b"></span><span class="c">' "<span class="a"></span><span class="b"></span><span class="c">" ar1 = $(div).find("span").get(); //=> [<span class="a"></span><span class="b"></span>,<span class="c"></span>] ar1.concat($(div).find("span").get()); //=> [<span class="a"></span><span class="b"></span>,<span class="c"></span>,<span class="a"></span><span class="b"></span>,<span class="c"></span>] jQuery.unique(ar1) //=> [<span class="a"></span><span class="b"></span>,<span class="c"></span>]

Now let's see scalar values:

var arr; // Number, true, String works arr = [1, 2, true, "hi", 3, 3, true, 2, 1, "hi"]; $.unique(arr); // => [3, "hi", true, 2, 1] // false and undefined doesn't work arr = [false, false, undefined, undefined]; $.unique(arr); // => [false, false, undefined, undefined] // null doesn't work, and will raise exception if the array contains anything other than null: arr = [null, null]; $.unique(arr); // => [null, null] arr = [null, null, false]; $.unique(arr); // => TypeError: Cannot read property 'compareDocumentPosition' of null // Object literals doens't work either, since they're allocated in different memory spaces arr = [{a:1, b:2}, {a:1, b:2}]; JSON.stringify($.unique(arr)); // => "[{"a":1,"b":2},{"a":1,"b":2}]"

Paul Irish hacked it to make 1.4.1's unique() possible to also deal with non-DOM Element objects, see: How to fulfill your own feature request -or- Duck Punching with jQuery! « Paul Irish

See also:

jQuery.unique() – jQuery API sizzle/sizzle.js#L994



In 1.4.1, to delay an animation from starting, this:

$(elem).delay(2000).fadeOut(); doesn't work because there is no queue, which delay requires.

$(elem).queue(function() { $(elem).delay(2000).fadeOut(); }); this would work.

I've tried 1.8.3 and it works without .queue wrapping. (feature detection)

48:12 – jQuery API src/support.js

jQuery Source Modules & Building


jQuery is constructed with many modules. We can build a jQuery with a small set of modules included. The jQuery team said in mid-2012 that there will be a jQuery build tool. It's been 2 years since Paul Irish's cast.

Nowadays people use jQuery from public CDNs. I think building my own jQuery and deliver it from my server means I cannot leverage public caching for jQuery JavaScript file, and pay delivering fee for it.

Episode 2

11 More Things I Learned from the jQuery Source - YouTube 11 More Things I Learned from the jQuery Source « Paul Irish 2011/01/19

jQuery initialization


It initializes in a local variable and then assign back to window.jQuery.

dom-dynamic-base detection

(I didn't get it.)

data-something with JSON / literal


<div data-awesome="true"></div> <div data-someobj='{ "best": "jQuery"}'></div>

In the source code it uses ?: conditional tree (bad-smell) to see if the string equals to true, false or null literals and return directly. If none matched, it tries to parse it as JSON. If JSON parsing failed, it returns the original string.

Technique about getElements


IE detection -- Inserts into a div, and test if a specifc i tag exist.

This is not how jQuery detects IE, Paul Irish just mentioned a technique used in it.


It actually is a NodeList, and updates lively as div element changes.


var ul = document.createElement("ul"); var items = div.getElementsByTagName("li"); ul.innerHTML += "<li>1</li>"; console.log(items); //=> [<li>1</li>] ul.innerHTML += "<li>2</li>"; console.log(items); //=> [<li>1</li>,<li>2</li>]

Also applies to getElementsByClassName, but does not apply to querySelectorAll.



When getting or setting CSS properties that are unsupported in the browser (e.g. opacity in IE6 or backgroundPositionY in Firefox), jQuery will fallback to browser-specific method according to cssHooks functions.

See also:

jQuery.cssHooks – jQuery API src/css.js#L497-L640

.css Auto Vendor-Prefixing


(I rarely use .css because I usually achieve it by Compass and alternating element's className to change the layout.)



jQuery.fn = jQuery.prototype;

This is why we can define a new plug-in by

jQuery.fn.myPlugin = function() {};