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