Jquery's overall architecture analysis and implementation examples

  • 2020-03-30 04:17:22
  • OfStack

The overall jQuery framework is very complex and difficult to read, and I have been working on this bulky and powerful framework for several days. The overall architecture of jQuery can be divided into: the entry module, the underlying module and the functional module. Here, we take jquery-1.7.1 as an example for analysis.

The overall architecture of jquery


16 (function( window, undefined ) {
         //Construct the jQuery object
  22     var jQuery = (function() {
  25         var jQuery = function( selector, context ) {
  27                 return new jQuery.fn.init( selector, context, rootjQuery );
  28             },
                 //A bunch of local variable declarations
  97         jQuery.fn = jQuery.prototype = {
  98             constructor: jQuery,
  99             init: function( selector, context, rootjQuery ) { ... },
                 //A bunch of stereotype properties and methods
 319         };
 322         jQuery.fn.init.prototype = jQuery.fn;
 324         jQuery.extend = jQuery.fn.extend = function() { ... };
 388         jQuery.extend({
                 //A bunch of static properties and methods
 892         });
 955         return jQuery;
 957     })();
          //Omit code for other modules... < br / > 9246     window.jQuery = window.$ = jQuery;
9266 })( window );

After analyzing the above code, we find that jquery adopts the method of anonymous function self-execution, which has the advantage of effectively preventing namespace and variable pollution. Abbreviate the above code:


(function(window, undefined) {
    var jQuery = function() {}
    // ...
    window.jQuery = window.$ = jQuery;
})(window);

Parameter window

The anonymous function passes in two arguments, window and undefined. As we know, variables are scoped in js, and these two variables will be passed in as local variables of anonymous functions, which will be faster to access. By passing in the window object, the window object can be used as a local variable, so that the parameters of the function also become local variables. When accessing the window object in jquery, there is no need to return the scope chain to the top-level scope, so that the window object can be accessed faster.

Undefined parameters

When js is looking for a variable, the js engine will first look for the variable in the scope of the function itself, and if it doesn't, it will keep looking up, and if it does, it will return the variable, and if it doesn't, it will return undefined. Undefined is an attribute of the window object, and you can shorten the scope chain for finding undefined by passing in the undefined parameter without assigning it. Within the scope of the self-calling anonymous function, make sure that undefined is really undefined. Because undefined can be overwritten, given a new value.

Jquery fn is what?


 jQuery.fn = jQuery.prototype = {
              constructor: jQuery,
              init: function( selector, context, rootjQuery ) { ... },
                 //A bunch of stereotype properties and methods
        };

After analyzing the above code, we found that jQuery. Fn is jQuery. Prototype. Later, we saw that jquery simply used a $sign instead of jquery for the sake of simplicity. Therefore, we often use $() when using jquery framework.

Constructor jQuery()

Picture description

JQuery objects are not created with new jQuery, but with new jQuery.fn.init:


var jQuery = function( selector, context ) {        return new jQuery.fn.init( selector, context, rootjQuery ); }

Here we define a variable, jQuery, whose value is the jQuery constructor, which is returned and assigned to the jQuery variable on line 955 (the top code)

JQuery. Fn. Init

JQuery. Fn (line 97 above) is the prototype object for the constructor jQuery(), and jQuery. Fn. init() is the jQuery prototype method, also known as the constructor. It is responsible for parsing the type of the argument selector and context and performing the corresponding lookup.

Context: can not pass in, or passed in jQuery objects, DOM elements, one of the ordinary js objects
RootjQuery: the jQuery object that contains the document object, used for document.getelementbyid () lookup failure, etc.


jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype
jQuery(selector [,context])

By default, the search for a matching element starts with the root element document object, which is the entire document tree, but you can also pass in a second parameter, context, to qualify its search. Such as:


$('div.foo').click(function () {
            $('span',this).addClass('bar');//Limit the search, that is, the context
above    });
jQuery.extend() and jQuery.fn.extend()

Methods jquery.extend (object) and jquery.fn.extend (object) are used to merge two or more objects into the first object. The relevant source code is as follows (part) :


jQuery.extend = jQuery.fn.extend = function() {
    var options, name, src, copy, copyIsArray, clone,//A set of defined local variables
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;

JQuery. The extend (object); Adding a class method to a jQuery class can be interpreted as adding static methods. Such as:


$.extend({
  add:function(a,b){returna+b;}
});

Add a "static method" called add to jQuery, and then you can use this method where jQuery was introduced,

$. The add (3, 4); / / return 7
JQuery. Fn.extend (object), see a section of the official website of the code demo as follows:


<label><input type="checkbox" name="foo"> Foo</label>
<label><input type="checkbox" name="bar"> Bar</label> <script>
    jQuery.fn.extend({
        check: function() {
            return this.each(function() {
                this.checked = true;
            });
        },
        uncheck: function() {
            return this.each(function() {
                this.checked = false;
            });
        }
    });
    // Use the newly created .check() method
    $( "input[type='checkbox']" ).check();
</script>

CSS selector engine Sizzle

JQuery was born to manipulate the DOM, so to speak. What makes jQuery so powerful is the CSS selector engine Sizzle.

The selector: "div > P + div. Aaron input [type = "checkbox]" "

Parsing rules:
1 from right to left
2 take out the last token  Such as [type = "checkbox"]
                                                      {
                                                              Matches: Array [3]
                                                              Type      : "ATTR"
                                                              Value    "[type ="
                                                              The checkbox "]"
                                                      }
3 filter type if type is > + ~ empty one of the four relationship selectors, skip and continue filtering
4 until the match is ID,CLASS,TAG  One, because it can be claimed through the browser's interface
5 now the seed collection has a value, shrinking the brush selection conditions to a minimum
6 if more than one matched seed collection requires further filtering, fix selector: "div > P + div. Aaron [type = "checkbox]" "
7 OK, skip to the stage of compiling the function

Deferred object

In the process of developing a website, we often encounter some time-consuming javascript operations. There are both asynchronous operations (such as ajax reading server data) and synchronous operations (such as traversing a large array) that do not yield immediate results.

It is common practice to specify callback functions for them. That is, specify in advance which functions should be called once they are finished running.

However, in terms of callback functions, jQuery is very weak. To change this, the jQuery development team designed deferred objects.

Simply put, deferred objects are jQuery's callback solution. In English, defer means "delay," so deferred object means "defer" until some future point.

To review the traditional way of writing ajax operations in jQuery:


$.ajax({
   url: "test.html",
   success: function(){
     alert(" Haha, it worked! ");
   },
   error:function(){
     alert(" Error! ");
   }
 });

In the above code, $.ajax() takes an object parameter that contains two methods: the success method specifies the callback function after the operation succeeds, and the error method specifies the callback function after the operation fails.

After the ajax() operation is completed, if you use jQuery which is less than 1.5.0 and return XHR object, you cannot do the chain operation. If higher than version 1.5.0, a deferred object is returned and chain operations can be performed.

Now, the new way of writing it is this:


$.ajax("test.html")
  .done(function(){ alert(" Haha, it worked! "); })
  .fail(function(){ alert(" Error! "); });

Specifies a callback function for multiple operations

Another great benefit of deferred objects is that they allow you to specify a single callback function for multiple events, which is not traditionally possible.

Look at the following code, which USES a new method $.when() :


$.when($.ajax("test1.html"), $.ajax("test2.html"))  .done(function(){ alert(" Haha, it worked! "); })  .fail(function(){ alert(" Error! "); });

What this code means is that you first perform two operations, $.ajax("test1.html") and $.ajax("test2.html"), and if both succeed, run the callback function specified by done(). If one or both fails, the callback function specified by fail() is executed.

JQuery.Deferred(func) implementation principle

Internally, three lists of callback functions are maintained: a list of successful callback functions, a list of failed callback functions, and a list of message callback functions, around which other methods operate and detect.

JQuery Deferred(func) source structure:


 jQuery.extend({     Deferred: function( func ) {
            //List of successful callback functions
        var doneList = jQuery.Callbacks( "once memory" ),
            //List of failed callback functions
            failList = jQuery.Callbacks( "once memory" ),
            //List of message callback functions
            progressList = jQuery.Callbacks( "memory" ),
            //Initial state
            state = "pending",
            //A read-only copy of the asynchronous queue
            promise = {
                // done, fail, progress
                // state, isResolved, isRejected
                // then, always
                // pipe
                // promise          
            },
            //Asynchronous queue
            deferred = promise.promise({}),
            key;
        //Method
to add a list of triggered success, failure, and message callbacks         for ( key in lists ) {
            deferred[ key ] = lists[ key ].fire;
            deferred[ key + "With" ] = lists[ key ].fireWith;
        }
        //Add a callback function to set the state
        deferred.done( function() {
            state = "resolved";
        }, failList.disable, progressList.lock )
        .fail( function() {
            state = "rejected";
        }, doneList.disable, progressList.lock );
        //If the function parameter func is passed in, execute. < br / >         if ( func ) {
            func.call( deferred, deferred );
        }         //Returns asynchronous queue deferred
        return deferred;
    },
}

JQuery. The when (deferreds)

Provides the ability to perform callback functions based on the state of one or more objects, typically based on an asynchronous queue with asynchronous events.

JQuery. When (deferreds)

If more than one asynchronous queue object is passed in, the method jquery.when () returns a read-only copy of the new main asynchronous queue object, which tracks the final state of the incoming asynchronous queue.

Once all asynchronous queues become successful, the successful callback function of the "main" asynchronous queue is called.

If one of the asynchronous queues becomes a failure state, the failure callback function for the main asynchronous queue is called.


/*
request '/when.do?method=when1' return {"when":1}
request '/when.do?method=when2' return {"when":2}
request '/when.do?method=when3' return {"when":3}
*/
var whenDone = function(){ console.log( 'done', arguments ); },
    whenFail = function(){ console.log( 'fail', arguments ); };
$.when(
    $.ajax( '/when.do?method=when1', { dataType: "json" } ),
    $.ajax( '/when.do?method=when2', { dataType: "json" } ),
    $.ajax( '/when.do?method=when3', { dataType: "json" } )
).done( whenDone ).fail( whenFail );

Picture description

Asynchronous queue delay

Decouple asynchronous tasks from callback functions

Provides basic functionality for ajax modules, queue modules, and ready events.

Stereotype properties and methods

Prototype properties and method source code:


  97 jQuery.fn = jQuery.prototype = {
  98     constructor: jQuery,
  99     init: function( selector, context, rootjQuery ) {}
 210     selector: "",
 213     jquery: "1.7.1",
 216     length: 0,
 219     size: function() {},
 223     toArray: function() {},
 229     get: function( num ) {},
 241     pushStack: function( elems, name, selector ) {},
 270     each: function( callback, args ) {},
 274     ready: function( fn ) {}, //
 284     eq: function( i ) {},
 291     first: function() {},
 295     last: function() {},
 299     slice: function() {},
 304     map: function( callback ) {},
 310     end: function() {},
 316     push: push,
 317     sort: [].sort,
 318     splice: [].splice
 319 };
 

The attribute selector is used to record selector expressions when jQuery looks up and filters DOM elements.
The.length property represents the number of elements in the current jquery object.
Method.size() returns the number of elements in the current jquery object, which is functionally equivalent to the property length, but should be preferred because it has no function call overhead.

. Size () source code is as follows:


size() : function(){
    return this.length;
}

ToArray () converts the current jQuery object into a real array, and the converted array contains all elements. The source code is as follows:


toArray: function() {
        return slice.call( this );
    },

Method. Get (index) returns the element at the specified location in the current jQuery object, or an array containing all the elements. Its source
Code is as follows:

Eg () and get() use detailed solutions : (link: #)

Method. Each () is used to iterate over the current jQuery object and perform a callback function on each element. Method. Each () is implemented internally by simply calling the static method jQuery. Each () :


each: function( callback, args ) {
        return jQuery.each( this, callback, args );
    },

The callback function is fired in the context of the current element, that is, the keyword this always points to the current element, and return false in the callback function can terminate the traversal.

Method. Map () traverses the current jQuery object, executes the callback function on each element, and puts the return value of the callback function into a new jQuery object. This method is often used to get or set the value of a collection of DOM elements.


map: function( callback ) {
        return this.pushStack( jQuery.map(this, function( elem, i ) {
            return callback.call( elem, i, elem );
        }));
    },

PushStack () creates a new empty jQuery object, then puts a collection of DOM elements into the jQuery object, keeping references to the current jQuery object.

The jQuery object traversal: eq (), first (), the last (), slice (), map ().

DOM lookup, filtering: the find (), not (), the filter (), closest (), add (), andSelf ().

DOM traversal:.parent(),.parents(),.parentsuntil (),.next(),.prev(),.nextall (),.prevall (),.nextunit (),.prevunit (),.siblings(),.children(),.contents().

DOM inserts: jQuery. Before (), jQuery. After (), jQuery. ReplaceWith (),.append(),.prepent(),.before(),.after(),.replacewith ().
Method. Push (elems, name, selector), which takes three arguments:

Parameter elems: an array of elements (or array like objects) that will be put into the new jQuery object.

Parameter name: the name of the jQuery method that produces the element array elems.

Selector: an argument passed to a jQuery method to correct the prototype property. Selector.
Method. End () ends the most recent filter operation in the current chain and restores the matching element to its previous state


end: function() {
        return this.prevObject || this.constructor(null);
    },

Returns the previous jQuery object and, if the attribute prevObect does not exist, builds an empty jQuery object to return. Method. PushStack () for pushing, method. End () for pushing

Static properties and methods

The relevant source code is as follows:


388 jQuery.extend({
 389     noConflict: function( deep ) {},
 402     isReady: false,
 406     readyWait: 1,
 409     holdReady: function( hold ) {},
 418     ready: function( wait ) {},
 444     bindReady: function() {},
 492     isFunction: function( obj ) {},
 496     isArray: Array.isArray || function( obj ) {},
 501     isWindow: function( obj ) {},
 505     isNumeric: function( obj ) {},
 509     type: function( obj ) {},
 515     isPlainObject: function( obj ) {},
 544     isEmptyObject: function( obj ) {},
 551     error: function( msg ) {},
 555     parseJSON: function( data ) {},
 581     parseXML: function( data ) {},
 601     noop: function() {},
 606     globalEval: function( data ) {},
 619     camelCase: function( string ) {},
 623     nodeName: function( elem, name ) {},
 628     each: function( object, callback, args ) {},
 669     trim: trim ? function( text ) {} : function( text ) {},
 684     makeArray: function( array, results ) {},
 702     inArray: function( elem, array, i ) {},
 724     merge: function( first, second ) {},
 744     grep: function( elems, callback, inv ) {},
 761     map: function( elems, callback, arg ) {},
 794     guid: 1,
 798     proxy: function( fn, context ) {},
 825     access: function( elems, key, value, exec, fn, pass ) {},
 852     now: function() {},
 858     uaMatch: function( ua ) {},
 870     sub: function() {},
 891     browser: {}
 892 });
 

To be continued,,,, today's first stop here, next time fill up. Don't worry, guys


Related articles: