When you read about an open source framework, what you want to learn most is the design ideas and implementation techniques.
Not much nonsense, jquery analysis has been bad for so many years, I have read it a long time ago,
However, in the past few years, I have been working on mobile terminals and have always used zepto. Recently, I took some time to scan jquery again
I will not translate the source code according to the script, so please read it based on your own actual experience!
The latest one on github is jquery-master, which has joined the AMD specification. I will refer to the official latest 2.0.3
Overall structure
The core of the jQuery framework is to match elements from HTML documents and perform operations on them,
For example:
At least 2 problems can be found from the above writing
1. How jQuery objects are constructed
2. How to call jQuery method
Analysis 1: jQuery’s new-free construction
JavaScript is a functional language. Functions can implement classes. Classes are the most basic concept in object-oriented programming
var a = new aQuery();
a.name();
This is a conventional usage method. It is obvious that jQuery does not work this way
jQuery does not use the new operator to instantiate jQuery display, or directly calls its function
Follow the writing method of jQuery
To achieve this, jQuery must be regarded as a class, then $() should return an instance of the class
So change the code:
Through new aQuery(), although an instance is returned, we can still see an obvious problem, an infinite loop!
So how to return a correct instance?
Instance this in javascript is only related to the prototype
Then you can use the jQuery class as a factory method to create instances, and put this method in the jQuery.prototye prototype
Instance returned when aQuery() is executed:
Obviously aQuery() returns an instance of the aQuery class, so this in init actually points to an instance of the aQuery class
The problem is that this in init points to the aQuery class. If the init function is also used as a constructor, how to deal with the internal this?
aQuery().age //18
In this case, something goes wrong, because this only points to the aQuery class, so you need to design an independent scope
JQuery framework separate scope processing
Obviously, through the instance init function, a new init instance object is constructed every time to separate this and avoid interaction confusion
So since they are not the same object, a new problem must arise
For example:
//Uncaught TypeError: Object [object Object] has no method 'name'
console.log(aQuery().name())
An error is thrown and this method cannot be found, so it is obvious that the init of new is separated from this of the jquery class
How to access the properties and methods on the jQuery class prototype?
How can we not only isolate the scope but also use the scope of the jQuery prototype object? Can we also access the jQuery prototype object in the returned instance?
Key points of implementation
Solve the problem through prototype passing, pass the jQuery prototype to jQuery.prototype.init.prototype
In other words, jQuery’s prototype object overrides the prototype object of the init constructor
Because it is passed by reference, there is no need to worry about the performance issue of this circular reference
Baidu borrowed a picture from a netizen for easy and direct understanding:
Explain fn, in fact, this fn has no special meaning, it is just a reference to jQuery.prototype
Analysis 2: Chain Call
Handling of DOM chain calls:
1. Save JS code.
2. The same object is returned, which can improve the efficiency of the code
Achieve cross-browser chain calls by simply extending the prototype method and returning this.
Use the simple factory pattern under JS to specify the same instance for all operations on the same DOM object.
This principle is super simple
Breaking down the code, it is obvious that the basic condition for realizing chaining is the existence of instance this, and it is the same
So we only need to access this in a chained method, because it returns this of the current instance, so we can access our own prototype
Advantages: save the amount of code, improve the efficiency of the code, and the code looks more elegant
The worst thing is that all object methods return the object itself, which means there is no return value, which may not be suitable in any environment.
Javascript is a non-blocking language, so it is not blocking, but it cannot block, so it needs to be driven by events and asynchronously complete some operations that need to block the process. This processing is only a synchronous chain, and an asynchronous chain jquery Promise has been introduced since 1.5, and jQuery.Deferred will be discussed later.
Analysis 3: Plug-in interface
The main framework of jQuery is like this, but according to the general designer's habits, if you want to add attribute methods to jQuery or jQuery prototype, and if you want to provide developers with method extensions, should they be provided from the perspective of encapsulation? An interface is the right one, and it can be understood literally that it is an extension of the function, rather than directly modifying the prototype. Friendly user interface,
jQuery supports its own extended properties. This provides an external interface, jQuery.fn.extend(), to add methods to objects
As you can see from the jQuery source code, jQuery.extend and jQuery.fn.extend are actually different references pointing to the same method
jQuery.extend extends the properties and methods of jQuery itself
jQuery.fn.extend extends the properties and methods of jQuery.fn
The extend() function can be used to easily and quickly extend functions without destroying jQuery’s prototype structure
jQuery.extend = jQuery.fn.extend = function(){...}; This is consecutive, that is, two points pointing to the same function, how can they achieve different functions? This is the power of this!
Fn and jQuery are actually two different objects, as described before:
When jQuery.extend is called, this points to the jQuery object (jQuery is a function and an object!), so the extension here is on jQuery.
When jQuery.fn.extend is called, this points to the fn object, jQuery.fn and jQuery.prototype point to the same object, and extending fn is to extend the jQuery.prototype prototype object.
What is added here is the prototype method, which is the object method. Therefore, the jQuery API provides the above two extension functions.
Implementation of extend
// Handle a deep copy situation
if ( typeof target === "boolean" ) { // 如果第一个参数为true,即 jQuery.extend( true, obj1, obj2 ); 的情况
deep = target; // 此时target是true
target = arguments[1] || {}; // target改为 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) ) { // 处理奇怪的情况,比如 jQuery.extend( 'hello' , {nick: 'casper})~~
target = {};
}
// extend jQuery itself if only one argument is passed
if ( length === i ) { // 处理这种情况 jQuery.extend(obj),或 jQuery.fn.extend( obj )
target = this; // jQuery.extend时,this指的是jQuery;jQuery.fn.extend时,this指的是jQuery.fn
--i;
}
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null ) { // 比如 jQuery.extend( obj1, obj2, obj3, ojb4 ),options则为 obj2、obj3...
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
// Prevent never-ending loop
if ( target === copy ) { // 防止自引用,不赘述
continue;
}
// Recurse if we're merging plain objects or arrays
// 如果是深拷贝,且被拷贝的属性值本身是个对象
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) { // 被拷贝的属性值是个数组
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
} else { 被拷贝的属性值是个plainObject,比如{ nick: 'casper' }
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// NEVER MOVE Original Objects, Clone Them
Target [name] = jquery.extEnd (deep, clone, copy); // Recursive ~
}
}
}
}
// Return the modified object
return target;
Summary:
By changing the pointing of the prototype pointer, let this new object also point to the prototype of the jQuery class prototype
So the object constructed in this way continues all the methods defined by jQuery.fn prototype