When I read an open-source framework, what I want to learn most is design ideas and implementation skills.
Not much nonsense. jquery has been writing bad Analysis for so many years and has read it before,
However, I have been working on mobile terminals over the past few years, and I have been using zepto. Recently, I took some time to scan jquery again.
I will not follow the Source translation of xuanke, and read it with my actual experience!
Jquery-master is the latest on github. After AMD specifications are added, I will refer to the latest official version 2.0.3.
Overall Architecture
The core of the jQuery framework is to match elements in HTML documents and perform operations on them,
For example:
Copy codeThe Code is as follows:
Detail ().find().css ()
Certificate ().hide().html ('...'). hide ().
At least two problems can be found in the above statement
1. jQuery Object Construction Method
2. Call method of jQuery Method
Analysis 1: No new construction of jQuery
JavaScript is a functional language, and functions can implement classes. Classes are the most basic concepts in object-oriented programming.
Copy codeThe Code is as follows:
Var aQuery = function (selector, context ){
// Constructor
}
AQuery. prototype = {
// Prototype
Name: function (){},
Age: function (){}
}
Var a = new aQuery ();
A. name ();
This is a conventional method of use. It is obvious that jQuery is not playing like this.
JQuery does not use the new operator to instantiate the jQuery display, or directly call its function.
Based on jQuery's Writing Method
Copy codeThe Code is as follows:
$ (). Ready ()
$ (). NoConflict ()
To achieve this, jQuery should be regarded as a class, so $ () should be the instance of the returned class.
So modify the code:
Copy codeThe Code is as follows:
Var aQuery = function (selector, context ){
Return new aQuery ();
}
AQuery. prototype = {
Name: function (){},
Age: function (){}
}
Through new aQuery (), although an instance is returned, it can also be seen that the obvious problem is that it is an endless loop!
So how to return a correct instance?
In javascript, this is only related to the prototype.
Then we can use the jQuery class as a factory method to create an instance and put this method in the jQuery. prototye prototype.
Copy codeThe Code is as follows:
Var aQuery = function (selector, context ){
Return aQuery. prototype. init ();
}
AQuery. prototype = {
Init: function (){
Return this;
}
Name: function (){},
Age: function (){}
}
When aQuery () is executed, the returned instance is:
Obviously, aQuery () returns an instance of the aQuery class, so this in init is actually an instance of the aQuery class.
The problem is that this of init points to the aQuery class. If the init function is also used as a constructor, what should we do internally with this?
Copy codeThe Code is as follows:
Var aQuery = function (selector, context ){
Return aQuery. prototype. init ();
}
AQuery. prototype = {
Init: function (){
This. age = 18
Return this;
},
Name: function (){},
Age: 20
}
AQuery (). age // 18
In this case, an error occurs. Because this only points to the aQuery class, you need to design an independent scope.
Processing of jQuery framework separation scopes
Copy codeThe Code is as follows:
JQuery = function (selector, context ){
// The jQuery object is actually just the init constructor 'enabled'
Return new jQuery. fn. init (selector, context, rootjQuery );
},
Obviously, through the instance init function, a new init instance object is created each time to separate this to avoid interaction confusion.
So since they are not the same object, there must be another new problem.
For example:
Copy codeThe Code is as follows:
Var aQuery = function (selector, context ){
Return new aQuery. prototype. init ();
}
AQuery. prototype = {
Init: function (){
This. age = 18
Return this;
},
Name: function (){},
Age: 20
}
// Uncaught TypeError: Object [object Object] has no method 'name'
Console. log (aQuery (). name ())
An error is thrown and this method cannot be found. Therefore, the init of new is obviously separated from this of jquery classes.
How to access the attributes and methods on the jQuery class prototype?
Can we isolate the scope, use the scope of the jQuery prototype object, and access the jQuery prototype object in the returned instance?
Key Points of implementation
Copy codeThe Code is as follows:
// Give the init function the jQuery prototype for later instantiation
JQuery. fn. init. prototype = jQuery. fn;
Solve the problem through prototype transfer and pass the jQuery prototype to jQuery. prototype. init. prototype
In other words, the prototype object of jQuery overwrites the prototype object of the init constructor.
Because it is a reference transfer, you do not need to worry about the performance of this circular reference.
Copy codeThe Code is as follows:
Var aQuery = function (selector, context ){
Return new aQuery. prototype. init ();
}
AQuery. prototype = {
Init: function (){
Return this;
},
Name: function (){
Return this. age
},
Age: 20
}
AQuery. prototype. init. prototype = aQuery. prototype;
Console. log (aQuery (). name () // 20
Baidu uses a picture from a netizen to help you understand the following:
In fn explanation, this fn does not have any special meaning, but it is just a reference of jQuery. prototype.
Analysis 2: chain call
Processing of DOM chained calls:
1. Save JS Code.
2. All returned objects are the same object, which can improve the code efficiency.
You can use the simple extension prototype method and return this to implement cross-browser chained calls.
The simple factory mode in JS is used to specify the same instance for all operations on the same DOM object.
This principle is super simple.
Copy codeThe Code is as follows:
AQuery (). init (). name ()
Decomposition
A = aQuery ();
A. init ()
A. name ()
By breaking down the code, it is obvious that the basic condition for implementing the chain is the existence of the instance this, and it is the same
Copy codeThe Code is as follows:
AQuery. prototype = {
Init: function (){
Return this;
},
Name: function (){
Return this
}
}
Therefore, you can access this by using a chained method. Because this is returned for the current instance, you can access your prototype.
Copy codeThe Code is as follows:
AQuery. init (). name ()
Advantage: the code volume is reduced, the code efficiency is improved, and the code looks more elegant.
The worst thing is that all object Methods return the object itself, that is, no return value, which is not necessarily applicable in any environment.
Javascript is a non-blocking language, so it is not blocking, but not blocking, so it needs to be driven by events, asynchronous to complete some operations that need to block the process, this process is just synchronous chain. asynchronous chain jquery introduced Promise and jQuery from 1.5. deferred will be discussed later.
Analysis 3: plug-in Interface
This is the main framework of jQuery. However, according to the habits of General designers, if you want to add attribute methods for jQuery or jQuery prototype, you also need to provide methods extension to developers, from the encapsulation point of view, should an interface be provided for access? Literally, it can be understood to be a Function Extension, rather than directly modifying prototype. friendly user interface,
JQuery supports attributes extension by itself. This provides an external interface, jQuery. fn. extend (), to add methods to objects.
From the jQuery source code, we can see that jQuery. extend and jQuery. fn. extend are actually different references pointing to the same method.
Copy codeThe Code is as follows:
JQuery. extend = jQuery. fn. extend = function (){
JQuery. extend extends jQuery's attributes and methods.
JQuery. fn. extend extends the attributes and methods of jQuery. fn.
Using the extend () function, you can quickly extend the function without damaging the jQuery prototype structure.
JQuery. extend = jQuery. fn. extend = function (){...}; this is a connection, that is, how can two functions point to the same function implement different functions? This is the power of this!
Fn and jQuery are actually two different objects, which are described earlier:
When jQuery. extend is called, this indicates the jQuery object (jQuery is a function and an object !), Therefore, this is extended on jQuery.
When jQuery. fn. extend is called, this points to the fn object. jQuery. fn and jQuery. prototype point to the same object. Extended fn is the extension of the jQuery. prototype object.
The prototype method is added here, that is, the object method. Therefore, jQuery APIs provide extended functions in above 2.
Implementation of extend
Copy codeThe Code is as follows:
JQuery. extend = jQuery. fn. extend = function (){
Var src, copyIsArray, copy, name, options, clone,
Target = arguments [0] | |{}, // common usage jQuery. extend (obj1, obj2). At this time, target is arguments [0]
I = 1,
Length = arguments. length,
Deep = false;
// Handle a deep copy situation
If (typeof target = "boolean") {// if the first parameter is true, that is, jQuery. extend (true, obj1, obj2 );
Deep = target; // The target is true at this time.
Target = arguments [1] | |{}; // change target to obj1
// Skip the boolean and the target
I = 2;
}
// Handle case when target is a string or something (possible in deep copy)
If (typeof target! = "Object "&&! JQuery. isFunction (target) {// handle strange situations, such as jQuery. extend ('hello', {nick: 'casper })~~
Target = {};
}
// Extend jQuery itself if only one argument is passed
If (length = I) {// handle this situation jQuery. extend (obj), or jQuery. fn. extend (obj)
Target = this; // when jQuery. extend is used, this indicates jQuery; When jQuery. fn. extend, this indicates jQuery. fn.
-- I;
}
For (; I <length; I ++ ){
// Only deal with non-null/undefined values
If (options = arguments [I])! = Null) {// For example, jQuery. extend (obj1, obj2, obj3, ojb4), options is obj2, obj3...
// Extend the base object
For (name in options ){
Src = target [name];
Copy = options [name];
// Prevent never-ending loop
If (target = copy) {// prevents self-reference.
Continue;
}
// Recurse if we're merging plain objects or arrays
// If it is a deep copy, and the copied property value itself is an object
If (deep & copy & (jQuery. isPlainObject (copy) | (copyIsArray = jQuery. isArray (copy )))){
If (copyIsArray) {// The copied property value is an array
CopyIsArray = false;
Clone = src & jQuery. isArray (src )? Src: [];
} Else {the copied property value is a plainObject, for example, {nick: 'casper '}
Clone = src & jQuery. isPlainObject (src )? Src :{};
}
// Never move original objects, clone them
Target [name] = jQuery. extend (deep, clone, copy); // recursive ~
// Don't bring in undefined values
} Else if (copy! = Undefined) {// specifies the shortest copy, and the attribute value is not undefined.
Target [name] = copy;
}
}
}
}
// Return the modified object
Return target;
Summary:
Using new jQuery. fn. init () to construct a new object, the method that owns the prototype object of the init Constructor
By changing the prorotype pointer, this new object also points to the prototype of the jQuery class.
So the constructed object continues all the methods defined by the jQuery. fn prototype.