Your SlideShare is downloading. ×
0
Recent Changes to
jQuery’s Internals
John Resig

September 12, 2009
No More Browser Sniffing


✤
    As of jQuery 1.3

✤
    jQuery.browser is now deprecated

✤
    All uses of jQuery.browse...
jQuery.support




✤   div.innerHTML = '   <link/><table></table><a href="/a"
    style="color:red;float:left;opacity:.5;"...
jQuery.support
    ✤    jQuery.support = {

!   !     // IE strips leading whitespace when .innerHTML is used
!   !     le...
Modularity
✤   Starting in 1.3.3 code is broken up even more

✤   Core has been split into many sub-modules

    ✤   core....
Modularity



✤
    Starting to reduce dependencies in-between files

✤
    Makes it easier to dynamically load portions of...
Mobile jQuery?

✤
    “Heavy” core.js

    ✤
        ready event

    ✤
        find() (powered by querySelectorAll)

    ✤...
Sizzle
✤
    Standalone selector engine, landed in jQuery 1.3.
                                                    Selecto...
Right to Left

✤
    Most selector engines (including jQuery, pre-1.3) evaluate a selector
    left to right

✤
    “#id d...
Reducing Complexity
Analyzing Performance

✤
    Optimizing performance is a huge concern: Faster code = happy
    users!

✤
    Measure execu...
Stack Profiling



✤
    jQuery Stack Profiler

✤
    Look for problematic methods and plugins

✤
    http://ejohn.org/blog...
FireUnit



✤
    A simple JavaScript test suite embedded in Firebug.

✤
    http://fireunit.org/
FireUnit Profile Data


                                       {
   fireunit.getProfile();                    "time": 8.443,...
Complexity Analysis
 ✤
     Analyze complexity rather than raw time

 ✤
     jQuery Call Count Profiler (uses FireUnit)
   ...
Complexity Analysis

✤
    Reducing call count helps to reduce complexity

✤
    Results for 1.3.3:

                   Me...
ECMAScript 5 Support


✤
    New in 1.3.3

✤
    Removal of arguments.callee

✤
    Switching from ‘RegExp to ‘new RegExp’...
Unified Syntax

✤
    Reusable inline functions and RegExp moved outside.
✤
    var rinlinejQuery = / jQueryd+="(?:d+|null...
Type Checks


    String: typeof object === "string"
•   Number: typeof object === "number"
•   Boolean: typeof object ===...
isFunction / isArray


✤
    isFunction overhauled in 1.3, isArray added in 1.3.3
✤



    toString = Object.prototype.toS...
Core
.selector / .context

✤
    Added in 1.3

✤
    Predominantly used for plugins

✤
    var div = jQuery(“div”);
    div.sel...
jQuery(...) unified


✤
    In 1.3.3

✤
    jQuery(null), jQuery(false), jQuery(undefined) => jQuery([])

✤
    jQuery() is...
jQuery(“TAG”)



✤
    In 1.3.3

✤
    jQuery(“body”) is now a shortcut for jQuery(document.body)

✤
    jQuery(“TAG”) is ...
Position

✤
    All in 1.3.3

✤
    .get(-N), .eq(-N)
    Access elements/jQuery sets using negative indices.

✤
    .first...
.data()


✤
    In 1.3.3

✤
    Calling .data() (no args) returns the full data object for an element

✤
    jQuery(“#foo”...
Selectors
Document Order
      if ( document.documentElement.compareDocumentPosition ) {
!     sortOrder = function( a, b ) {
!     ...
:hidden / :visible

✤
     Changed in 1.3.2, revised in 1.3.3

✤
     Changed logic of :hidden/:visible to use .offsetWidt...
ID-rooted Queries

✤
    Changed in 1.3.3

✤
    $(“#id div”) sped up dramatically

✤
    #id is detected and used as the ...
DOM Manipulation
HTML Fragments

✤
    Overhauled in 1.3

✤
    .append(“<li>foo</li>”) first converts HTML to a DOM fragment

✤
    Fragmen...
HTML Fragments (redux.)

✤
    In 1.3.3

✤
    It turns out that using fragments makes it really easy to cache them,
    a...
HTML Fragments

✤
    Surprising change:

✤
    for ( var i = 0; i < 250; i++ ) {
      $(“<li>some thing</li>”).attr(“id”...
$(“<div/>”)


✤
    In 1.3:

✤
    $(“<div/>”) was made equivalent to $(document.createElement
    (“div”))

✤
    In 1.3....
append(function(){ })
✤
    In 1.3.3 append, prepend, before, after, wrap, wrapAll, replace, replaceAll all
    take a fun...
.detach()


✤
    In 1.3.3

✤
    Like .remove() but leaves events and data intact.
✤   detach: function( selector ) {


!...
DOM Traversal
.closest()


✤
    Added in 1.3

✤
    $(this).closest(“div”) checks if ‘this’ is a div, if not keeps going up the
    tre...
find() perf

✤
    In 1.3.3

✤
    Reduced the number of function calls - Reduced 16 calls per find() to 5.
✤   find: funct...
find() perf




✤
    Perf for $(“...”) improved, as well (hooked into rootQuery, uses less
    function calls)
.not() / .filter()



✤
    .not(function(){}) (1.3.3)

✤
    .filter(Element), .filter(function(){}) (1.3.3)

✤
    Full AP...
.index()



✤
    In 1.3.3

✤
    $(“div”).index() - position of element relative to siblings

✤
    $(“#foo”).index(“div”...
Events
Live Events


✤
    Super-efficient event delegation - uses .closest(), introduced in 1.3.

✤
    1.3.3 adds context and da...
.bind() `thisObject`


✤
    In 1.3.3

✤
    You can now bind functions enforcing this `this`

✤
    var obj = { method: f...
Dynamic Ready Event



✤
    In 1.3.3

✤
    document.readyState is checked to determine if the body is already
    loaded...
Method                                      Calls      O(N)
.addClass("test");                                            ...
Method                                    Calls      O(N)
.addClass("test");                                              ...
Recent Changes to jQuery's Internals
Recent Changes to jQuery's Internals
Upcoming SlideShare
Loading in...5
×

Recent Changes to jQuery's Internals

21,268

Published on

A talk I gave at the 2009 jQuery Conference.

Published in: Technology, Spiritual
2 Comments
35 Likes
Statistics
Notes
No Downloads
Views
Total Views
21,268
On Slideshare
0
From Embeds
0
Number of Embeds
13
Actions
Shares
0
Downloads
536
Comments
2
Likes
35
Embeds 0
No embeds

No notes for slide

Transcript of "Recent Changes to jQuery's Internals"

  1. 1. Recent Changes to jQuery’s Internals John Resig September 12, 2009
  2. 2. No More Browser Sniffing ✤ As of jQuery 1.3 ✤ jQuery.browser is now deprecated ✤ All uses of jQuery.browser have been removed from core. ✤ jQuery.support replaces it.
  3. 3. jQuery.support ✤ div.innerHTML = ' <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a </a><select><option>text</option></select>';
  4. 4. jQuery.support ✤ jQuery.support = { ! ! // IE strips leading whitespace when .innerHTML is used ! ! leadingWhitespace: div.firstChild.nodeType == 3, ! ! // Make sure that tbody elements aren't automatically inserted ! ! // IE will insert them into empty tables ! ! tbody: !div.getElementsByTagName("tbody").length, ! ! // Make sure that link elements get serialized correctly by innerHTML ! ! // This requires a wrapper element in IE ! ! htmlSerialize: !!div.getElementsByTagName("link").length, ! ! // Get the style information from getAttribute ! ! // (IE uses .cssText insted) ! ! style: /red/.test( a.getAttribute("style") ), ! ! // Make sure that URLs aren't manipulated ! ! // (IE normalizes it by default) ! ! hrefNormalized: a.getAttribute("href") === "/a", ! ! // Make sure that element opacity exists ! ! // (IE uses filter instead) ! ! opacity: a.style.opacity === "0.5", ! ! // Verify style float existence ! ! // (IE uses styleFloat instead of cssFloat) ! ! cssFloat: !!a.style.cssFloat, ! ! // Will be defined later ! ! scriptEval: false, ! ! noCloneEvent: true, ! ! boxModel: null ! };
  5. 5. Modularity ✤ Starting in 1.3.3 code is broken up even more ✤ Core has been split into many sub-modules ✤ core.js ✤ attributes.js ✤ css.js ✤ manipulation.js ✤ traversing.js
  6. 6. Modularity ✤ Starting to reduce dependencies in-between files ✤ Makes it easier to dynamically load portions of the library ✤ (Mobile jQuery!)
  7. 7. Mobile jQuery? ✤ “Heavy” core.js ✤ ready event ✤ find() (powered by querySelectorAll) ✤ filter() (very basic functionality) ✤ $.getScript() (for loading modules) ✤ Still experimenting...
  8. 8. Sizzle ✤ Standalone selector engine, landed in jQuery 1.3. Selector % Used # of Uses ✤ Built for selectors that people actually use. #id 51.290% 1431 .class 13.082% 365 tag 6.416% 179 tag.class 3.978% 111 #id tag 2.151% 60 tag#id 1.935% 54 #id:visible 1.577% 44 #id .class 1.434% 40 .class .class 1.183% 33 * 0.968% 27 #id tag.class 0.932% 26 #id:hidden 0.789% 22 tag[name=value] 0.645% 18 .class tag 0.573% 16 [name=value] 0.538% 15 tag tag 0.502% 14 #id #id 0.430% 12 #id tag tag 0.358% 10
  9. 9. Right to Left ✤ Most selector engines (including jQuery, pre-1.3) evaluate a selector left to right ✤ “#id div” - finds the element by ID “id” then finds all divs inside of it. ✤ Sizzle finds all divs then checks to see if theres an ancestor with an id of “id”. ✤ How CSS engines work in browsers. ✤ Only one query per selector, rest is filtering.
  10. 10. Reducing Complexity
  11. 11. Analyzing Performance ✤ Optimizing performance is a huge concern: Faster code = happy users! ✤ Measure execution time ✤ Loop the code a few times ✤ Measure the difference: ✤ (new Date).getTime();
  12. 12. Stack Profiling ✤ jQuery Stack Profiler ✤ Look for problematic methods and plugins ✤ http://ejohn.org/blog/deep-profiling-jquery-apps/
  13. 13. FireUnit ✤ A simple JavaScript test suite embedded in Firebug. ✤ http://fireunit.org/
  14. 14. FireUnit Profile Data { fireunit.getProfile(); "time": 8.443, "calls": 611, "data":[ { "name":"makeArray()", "calls":1, "percent":23.58, "ownTime":1.991, "time":1.991, "avgTime":1.991, "minTime":1.991, http://ejohn.org/blog/function-call- "maxTime":1.991, "fileName":"jquery.js (line 2059)" profiling/
  15. 15. Complexity Analysis ✤ Analyze complexity rather than raw time ✤ jQuery Call Count Profiler (uses FireUnit) Method Calls Big-O .addClass("test"); 542 6n .addClass("test"); 592 6n .removeClass("test"); 754 8n .removeClass("test"); 610 6n .css("color", "red"); 495 5n .css({color: "red", border: "1px solid red"}); 887 9n .remove(); 23772 2n+n2 .append("<p>test</p>"); 307 3n
  16. 16. Complexity Analysis ✤ Reducing call count helps to reduce complexity ✤ Results for 1.3.3: Method Calls Big-O .remove(); 298 3n .html("<p>test</p>"); 507 5n .empty(); 200 2n http://ejohn.org/blog/function-call-
  17. 17. ECMAScript 5 Support ✤ New in 1.3.3 ✤ Removal of arguments.callee ✤ Switching from ‘RegExp to ‘new RegExp’ ✤ Using JSON.parse, where available. ✤ Support for json2.js is auto-baked in.
  18. 18. Unified Syntax ✤ Reusable inline functions and RegExp moved outside. ✤ var rinlinejQuery = / jQueryd+="(?:d+|null)"/g, ! rleadingWhitespace = /^s+/, ! rsingleTag = /^<(w+)s*/?>$/, ! rxhtmlTag = /(<(w+)[^>]*?)/>/g, ! rselfClosing = /^(?:abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i, ! rinsideTable = /^<(thead|tbody|tfoot|colg|cap)/, ! rtbody = /<tbody/i, ! fcloseTag = function(all, front, tag){ ! ! return rselfClosing.test(tag) ? ! ! ! all : ! ! ! front + "></" + tag + ">"; ! }; ✤ http://docs.jquery.com/JQuery_Core_Style_Guidelines
  19. 19. Type Checks String: typeof object === "string" • Number: typeof object === "number" • Boolean: typeof object === "boolean" • Object: typeof object === "object" • Function: jQuery.isFunction(object) • Array: jQuery.isArray(object) • Element: object.nodeType • null: object === null • undefined: typeof variable === "undefined" or object.prop === undefined • null or undefined: object == null
  20. 20. isFunction / isArray ✤ isFunction overhauled in 1.3, isArray added in 1.3.3 ✤ toString = Object.prototype.toString isFunction: function( obj ) { ! ! return toString.call(obj) === "[object Function]"; ! }, ! isArray: function( obj ) { ! ! return toString.call(obj) === "[object Array]"; ! },
  21. 21. Core
  22. 22. .selector / .context ✤ Added in 1.3 ✤ Predominantly used for plugins ✤ var div = jQuery(“div”); div.selector === “div”; div.context === document; ✤ jQuery.fn.update = function(){ return this.pushStack( jQuery(this.selector, this.context), “update” ); };
  23. 23. jQuery(...) unified ✤ In 1.3.3 ✤ jQuery(null), jQuery(false), jQuery(undefined) => jQuery([]) ✤ jQuery() is still the same as jQuery(document) (for now) ✤ BUT jQuery(“div”) points back to a common jQuery(document) root. ✤ jQuery(“div”).prevObject === jQuery()
  24. 24. jQuery(“TAG”) ✤ In 1.3.3 ✤ jQuery(“body”) is now a shortcut for jQuery(document.body) ✤ jQuery(“TAG”) is optimized (skips Sizzle)
  25. 25. Position ✤ All in 1.3.3 ✤ .get(-N), .eq(-N) Access elements/jQuery sets using negative indices. ✤ .first(), .last() Shortcuts for .eq(0) and .eq(-1) ✤ .toArray() Shortcut for .get()
  26. 26. .data() ✤ In 1.3.3 ✤ Calling .data() (no args) returns the full data object for an element ✤ jQuery(“#foo”).data(“a”, 1).data(“b”, 2).data() => { a: 1, b: 2 }
  27. 27. Selectors
  28. 28. Document Order if ( document.documentElement.compareDocumentPosition ) { ! sortOrder = function( a, b ) { ! ! var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; ! ! if ( ret === 0 ) { ! ! ! hasDuplicate = true; ! ! } ! ! return ret; ! }; } else if ( "sourceIndex" in document.documentElement ) { ! sortOrder = function( a, b ) { ! ! var ret = a.sourceIndex - b.sourceIndex; ! ! if ( ret === 0 ) { ! ! ! hasDuplicate = true; ! ! } ! ! return ret; ! }; } else if ( document.createRange ) { ! sortOrder = function( a, b ) { ! ! var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); ! ! aRange.selectNode(a); ! ! aRange.collapse(true); ! ! bRange.selectNode(b); ! ! bRange.collapse(true); ! ! var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); ! ! if ( ret === 0 ) { ! ! ! hasDuplicate = true; ! ! } ! ! return ret; ! } }; ✤ As of 1.3.2 all selectors return in document order (as if you did getElementsByTagName(“*”))
  29. 29. :hidden / :visible ✤ Changed in 1.3.2, revised in 1.3.3 ✤ Changed logic of :hidden/:visible to use .offsetWidth === 0 && .offsetHeight === 0 trick ✤ Sizzle.selectors.filters.hidden = function(elem){ ! var width = elem.offsetWidth, height = elem.offsetHeight, ! ! force = /^tr$/i.test( elem.nodeName ); // ticket #4512 ! return ( width === 0 && height === 0 && !force ) ? ! ! true : ! ! ! ( width !== 0 && height !== 0 && !force ) ? ! ! ! ! false : ! ! ! ! ! !!( jQuery.curCSS(elem, "display") === "none" ); };
  30. 30. ID-rooted Queries ✤ Changed in 1.3.3 ✤ $(“#id div”) sped up dramatically ✤ #id is detected and used as the root of the query ✤ $(“div”, “#id”) --> $(“#id”).find(“div”) ✤ $(“#id div”) can likely never be as fast as $(“#id”).find(“div”) ✤ $(“#id”) is so hyper-optimized, it’s hard to match raw perf.
  31. 31. DOM Manipulation
  32. 32. HTML Fragments ✤ Overhauled in 1.3 ✤ .append(“<li>foo</li>”) first converts HTML to a DOM fragment ✤ Fragments act as a loose collection for DOM nodes ✤ Can be used to insert many nodes with a single operation ✤ var fragment = document.createDocumentFragment(); while ( node.firstChild ) fragment.appendChild( node.firstChild ); document.body.appendChild( node );
  33. 33. HTML Fragments (redux.) ✤ In 1.3.3 ✤ It turns out that using fragments makes it really easy to cache them, as well. ✤ Look for common cases that can be cached (simple, small, HTML strings that are built more than once). ✤ Also optimized $(“<some html>”), cut out some unnecessary intermediary code. ✤ jQuery.fragments[“<some html>”] = fragment;
  34. 34. HTML Fragments ✤ Surprising change: ✤ for ( var i = 0; i < 250; i++ ) { $(“<li>some thing</li>”).attr(“id”, “foo” + i).appendTo(“ul”); } ✤ Now faster than: ✤ for ( var i = 0; i < 250; i++ ) { $(“ul”).append(“<li id=‘foo” + i + “‘>some thing</li>”); }
  35. 35. $(“<div/>”) ✤ In 1.3: ✤ $(“<div/>”) was made equivalent to $(document.createElement (“div”)) ✤ In 1.3.3: ✤ Logic for handling createElement moved to jQuery() (performance improved!)
  36. 36. append(function(){ }) ✤ In 1.3.3 append, prepend, before, after, wrap, wrapAll, replace, replaceAll all take a function as an argument ✤ Compliments .attr(“title”, function(){}) (and CSS) ✤ Makes: $(“li”).each(function(){ $(this).append(“My id: “ + this.id); }); ✤ Also: $(“li”).append(function(){ return “My id: “ + this.id; });
  37. 37. .detach() ✤ In 1.3.3 ✤ Like .remove() but leaves events and data intact. ✤ detach: function( selector ) { ! ! return this.remove( selector, true ); ! },
  38. 38. DOM Traversal
  39. 39. .closest() ✤ Added in 1.3 ✤ $(this).closest(“div”) checks if ‘this’ is a div, if not keeps going up the tree until it finds the closest one. ✤ In 1.3.3 ✤ $(this).closest(“.test”, document.body) Prevent the traversal from going too far up the tree.
  40. 40. find() perf ✤ In 1.3.3 ✤ Reduced the number of function calls - Reduced 16 calls per find() to 5. ✤ find: function( selector ) { ! ! var ret = this.pushStack( "", "find", selector ), length = 0; ! ! for ( var i = 0, l = this.length; i < l; i++ ) { ! ! ! length = ret.length; ! ! ! jQuery.find( selector, this[i], ret ); ! ! ! if ( i > 0 ) { ! ! ! ! // Make sure that the results are unique ! ! ! ! for ( var n = length; n < ret.length; n++ ) { ! ! ! ! ! for ( var r = 0; r < length; r++ ) { ! ! ! ! ! ! if ( ret[r] === ret[n] ) { ! ! ! ! ! ! ! ret.splice(n--, 1); ! ! ! ! ! ! ! break; ! ! ! ! ! ! } ! ! ! ! ! } ! ! ! ! } ! ! ! } ! ! } ! ! return ret; ! },
  41. 41. find() perf ✤ Perf for $(“...”) improved, as well (hooked into rootQuery, uses less function calls)
  42. 42. .not() / .filter() ✤ .not(function(){}) (1.3.3) ✤ .filter(Element), .filter(function(){}) (1.3.3) ✤ Full API parity inbetween .not() and .filter()
  43. 43. .index() ✤ In 1.3.3 ✤ $(“div”).index() - position of element relative to siblings ✤ $(“#foo”).index(“div”) - position relative to all divs
  44. 44. Events
  45. 45. Live Events ✤ Super-efficient event delegation - uses .closest(), introduced in 1.3. ✤ 1.3.3 adds context and data object support. ✤ 1.3.3 will ship once “submit”, “change”, and “focus/blur” events work in .live() (in all browsers).
  46. 46. .bind() `thisObject` ✤ In 1.3.3 ✤ You can now bind functions enforcing this `this` ✤ var obj = { method: function(){} }; $(“div”).bind( “click”, function(){ this.objProp = true; }, obj );
  47. 47. Dynamic Ready Event ✤ In 1.3.3 ✤ document.readyState is checked to determine if the body is already loaded - if so, no need to wait for ready event.
  48. 48. Method Calls O(N) .addClass("test"); 542 O(6n) .addClass("test"); 592 O(6n) .removeClass("test"); 754 O(8n) .removeClass("test"); 610 O(6n) .css("color", "red"); 495 O(5n) .css({color: "red", border: "1px solid red"}); 887 O(9n) .remove(); 23772 O(2n+n2) .append("<p>test</p>"); 307 O(3n) .append("<p>test</p><p>test</p><p>test</p><p>test</p><p>test</p>"); 319 O(3n) .show(); 394 O(4n) .hide(); 394 O(4n) .html("<p>test</p>"); 28759 O(3n+n2) .empty(); 28452 O(3n+n2) .is("div"); 110 .filter("div"); 216 O(2n) .find("div"); 1564 O(16n)
  49. 49. Method Calls O(N) .addClass("test"); 2 .addClass("test"); 2 .removeClass("test"); 2 .removeClass("test"); 2 .css("color", "red"); 102 .css({color: "red", border: "1px solid red"}); 201 O(2n) .remove(); 299 O(3n) .append("<p>test</p>"); 116 .append("<p>test</p><p>test</p><p>test</p><p>test</p><p>test</p>"); 128 .show(); 394 O(4n) .hide(); 394 O(4n) .html("<p>test</p>"); 100 .empty(); 201 O(2n) .is("div"); 109 .filter("div"); 216 O(2n) .find("div"); 495 O(5n)
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×