[Simple introduction to jQuery] source code analysis-overall architecture, simple introduction to jquery

Source: Internet
Author: User

[Simple introduction to jQuery] source code analysis-overall architecture, simple introduction to jquery

I have been studying jQuery Source Code recently. I have no clue when I first look at the source code. It is really subtle to let me sit down and look at it carefully, so that you can lament the beauty of the Code.

Its clear structure, high cohesion, low coupling, excellent performance and convenient scalability, browser compatibility (functional defects, progressive enhancement) elegant processing capabilities, Ajax, and other thoughtful and powerful customization functions are all amazing.

In addition, reading the source code gives me a lot of underlying knowledge. I have a new understanding of native JS, framework design, and code optimization. Next I will write a series of articles on jQuery parsing.

I have a full-text annotation on jQuery source code on github. If you are interested, you can view it. JQuery v1.10.2 source code annotation.


There are already a lot of articles on jQuery source code on the Internet. As the first article in the series, I want to start a title [simple jQuery] with a low level of qualifications, there is no way to analyze jQuery, but there is indeed a lot of clever design in jQuery source code, and readers at different levels can gain some benefits, therefore, we plan to share some of the knowledge points we have learned. It is intended to be analyzed in chapters from the whole and its branches. This article focuses on the overall architecture of jQuery and some preparations. Let's take a look at the overall structure of jQuery:


Overall Architecture


Unlike the obscure implementation of each module of jQuery code, the structure of jQuery's overall framework is very clear, which is roughly divided into modules as shown in.

The jQuery source code may be confusing at first, because the 9000 lines of code feel endless, it is very important to understand the author's ideas.

On the whole, I think jQuery usesTotal -- pointsAlthough JavaScript has a mechanism for improving the scope, more than 9000 lines of code do not mean that all variables must be defined at the top of the table for mutual associativity. In jQuery, only global applications are used.Variable and Regular ExpressionIt is defined at the beginning of the Code. At the beginning of each module, some variables, regular expressions, and methods used only in this module are defined. Therefore, in the first reading process, there will be many variables, regular expressions, and methods that do not understand their functions.

Therefore, I think it is very important to read the source code to abandon the process-oriented approach of thinking and not to deliberately pursue the need to understand every sentence from top to bottom. It is very likely that you get stuck in a strange method or variable at the beginning and want to know the role of this method or variable. However, it may take thousands of rows for it to be called. If you pursue this way of making it clear by words, it is very likely that the enthusiasm for reading will be greatly affected after several hits.

The truth is that there are a lot of cases. Let's take a look at the real text and analyze some preliminary preparations and small details of jQurey:


Closure Structure

// Wrap it with a function domain, which is the so-called sandbox // The variable defined by var here, which belongs to the local variable in this function domain, avoid global contamination // introduce the external variables required by the current sandbox through function parameters // as long as the consistency of the interfaces provided by the parameters internally is ensured, you can also replace the passed parameter (function (window, undefined) {// jQuery code}) (window );

The specific implementation of jQuery is included in a closure constructed by the immediate execution function. In order not to pollute the global scope, only the variables $ and jQuery are exposed to the outside world, avoid variable conflicts whenever possible. There is another commonly used method:

(Function (window) {// JS Code}) (window, undefined );

The first method is jQuery. What's the difference between the two when our code runs in an earlier environment (pre-ES5, eg. internet Explorer 8), undefined is only a variable and its value can be overwritten. This means you can perform the following operations:

undefined = 42console.log(undefined) // 42

When using the first method, you can ensure that the undefined you need is indeed undefined.

In addition, jQuery has a compression optimization detail here. The first method is used when the code is compressed, both window and undefined can be compressed into one letter and ensure that they are windows and undefined.

// Compression policy // w-> windwow, u-> undefined (function (w, u) {}) (window );


No new construction

Hey, let's look back at how to instantiate a jQuery object when using jQuery:

// No new constructor $ ('# test '). text ('test'); // you can also use newvar Test = new $ ('# test'); test. text ('test ');

When using jQuery, most people use the first non-new constructor to directly construct $ (''). This is also a very convenient part of jQuery. When we use the first non-new constructor, its essence is equivalent to new jQuery (). How is it implemented inside jQuery? Let's see:

(Function (window, undefined) {var //... jQuery = function (selector, context) {// The jQuery object is actually just the init constructor 'enabled' // here, The instantiation method jQuery () actually called the extended Prototype Method jQuery. fn. initreturn new jQuery. fn. init (selector, context, rootjQuery);}, // jQuery. prototype is the prototype of jQuery. After the above method is mounted, all generated jQuery objects can use jQuery. fn = jQuery. prototype = {// instantiation method. This method can be called jQuery object constructor init: function (selector, context, rootjQuery ){//...}} // This sentence is very important, and it is also very difficult. // jQuery does not use the new operator to instantiate jQuery, but directly calls its function. // to implement this, jQuery should be regarded as a class, and return a correct instance // and the instance must be able to correctly access the attributes and methods on the jQuery class prototype. // The jQuery method solves the problem through prototype transfer, pass the jQuery prototype to jQuery. prototype. init. prototype // so the instance this generated by this method still points to jQuery. therefore, you can correctly access the attributes and methods of the jQuery class prototype jQuery. fn. init. prototype = jQuery. fn ;}) (window );

Most people will get stuck in jQuery. fn. init. prototype = jQuery. fn, which is hard to understand. But this sentence is really a wonderful thing about jQuery. It is very important to understand these sentences. Let's take a look at the following points:

1) First of all, we need to make it clear that with the $ ('xxx') instantiation method, the internal call is return new jQuery. fn. init (selector, context, rootjQuery), that is, the Construction Instance is handed over to jQuery. fn. the init () method is obtained completely.

2) convert jQuery. fn. set the prototype attribute of init to jQuery. fn, use new jQuery. fn. the prototype object of the object generated by init () is jQuery. fn, so mount it to jQuery. the function above fn is equivalent to mounting to jQuery. fn. all jQuery objects generated by init () use new jQuery. fn. the object generated by init () can also access jQuery. all prototype methods on fn.

3) that is, the instantiation method has such a relationship chain.

  • JQuery. fn. init. prototype = jQuery. fn = jQuery. prototype;
  • New jQuery. fn. init () is equivalent to new jQuery ();
  • JQuery () returns new jQuery. fn. init (), while var obj = new jQuery (), so the two are equivalent, so we can instantiate the jQuery object without new.


Method Overloading

Another reason why jQuery's source code is obscure is that it uses a large number of methods to overload it, but it is very convenient to use it:

// Obtain the value of the title attribute $ ('# id '). attr ('title'); // set the value of the title attribute $ ('# id '). attr ('title', 'jquery '); // obtain the value of a css keyword ('your id' example .css ('title'); // set the value of a css keyword ('your id' example .css ('width', '200px ');

Method Overloading is a method that implements multiple functions, often get and set. Although it is not easy to read, it is also a reason why jQuery is so popular from a practical perspective, most people use the jQuery () constructor to directly instantiate a jQuery object. However, in its internal implementation, there are nine different methods for overload scenarios:

// Receives a string containing the CSS selector jQuery ([selector, [context]) used to match the element set. // imports a single DOM jQuery (element) // input the DOM array jQuery (elementArray) // input the JS object jQuery (object) // input the jQuery object jQuery (jQuery object) // input the original HTML string to create the DOM element jQuery (html, [ownerDocument]) jQuery (html, [attributes]) // input the null parameter jQuery () // bind a function jQuery (callback) executed after the DOM file is loaded)

So when reading the source code, it is very important to read it with jQuery API to understand how many methods are reloaded. What I want to say is, some methods of jQuery source code are very long and cumbersome to implement, because jQuery itself, as a framework with strong versatility, is compatible with many situations and allows users to input different parameters, as a result, the internal processing logic is very complex. Therefore, when interpreting a method, it is obviously difficult to try to jump out of the Code itself, from a higher dimension, there will be some difference in thinking about the complex logic for processing or compatibility, whether it is heavy load, and why it is written like this. Secondly, for this reason, jQuery source code has many HACK compatible with lower versions or complicated code snippets with obscure logic, browser compatibility makes it extremely easy for a front-end engineer to learn the essence of programming, so do not be too persistent in some aspects. Even if compatibility is very important, you should also learn and understand it in a moderate manner.


JQuery. fn. extend and jQuery. extend

The extend method is a very important method in jQuery. jQuey internally uses it to expand static methods or instance methods, and we will also use it when developing jQuery plug-ins. However, there are two extend methods, jQuery. fn. extend and jQuery. extend, which are a key part of understanding jQuery. Conclusion:

1)JQuery. extend (object)Add a new static method to the class to expand the jQuery class itself;

2)JQuery. fn. extend (object)Add an instance method to the jQuery object, that is, the new method added by this extend. All instantiated jQuery objects can be used because they are mounted to jQuery. fn (jQuery. fn = jQuery. prototype ).

Their official explanation is:

1) jQuery. extend (): combines two or more objects into the first one,

2) jQuery. fn. extend (): attaches an object to the prototype attribute of jQuery to extend a new jQuery instance method.

That is to say, using jQuery. extend () to expand the static method, we can directly call $. xxx (xxx is the name of the extended method ),

You need to use $ (). xxx to call the extended instance method using jQuery. fn. extend.

The source code parsing is long. You can click here to expand it or read it here:

// Extended merging function // merge the attributes of two or more objects to the first object. Most of jQuery's subsequent functions are extended through this function // although the implementation method is the same, however, note that the usage is different. Why does the two methods point to the same function implementation but implement different functions, // read the source code and you will find this is due to the power of this. // if two or more objects are input, the attributes of all objects are added to the first target object. // if only one object is input, the attributes of the object are added to the jQuery object, that is, to add a static method // in this way, we can add a new method to the jQuery namespace, which can be used to compile the jQuery plug-in // if you do not want to change the input object, you can input an empty object: $. extend ({}, object1, object2); // The default merge operation does not iterate, even if a target attribute is an object or attribute, it will also be completely overwritten rather than merged // if the first parameter is true, it will be a deep copy // Attributes inherited from the object prototype will be copied, and attributes with the value of undefined will not be copied. // for performance reasons, attributes of the built-in JavaScript type will not be merged with jQuery. extend = jQuery. fn. extend = function () {var src, copyIsArray, copy, name, options, clone, target = arguments [0] |{}, I = 1, length = arguments. length, deep = false; // Handle a deep copy situation // target is the first input parameter // if the first parameter is of the boolean type, it indicates whether deep recursion is required, if (typeof target = "boolean") {deep = target; target = arguments [1] |{}; // ski P the boolean and the target // if the first parameter of the boolean type is passed, I starts from 2 and I = 2 ;} // Handle case when target is a string or something (possible in deep copy) // if the first parameter is a string or another if (typeof target! = "Object "&&! JQuery. isFunction (target) {target ={};}// extend jQuery itself if only one argument is passed // if the parameter length is 1, jQuery static method if (length = I) {target = this; -- I ;} // multiple replication sources can be input. // I is for (; I <length; I ++) starting from 1 or 2) {// Only deal with non-null/undefined values // copy all the attributes of each source to the target if (options = arguments [I])! = Null) {// Extend the base objectfor (name in options) {// src is the source (itself) the value of // copy is the value of src = target [name]; copy = options [name]; // Prevent never-ending loop // prevents loops, for example, extend (true, target, {'target': target}); if (target = copy) {continue ;} // Recurse if we're merging plain objects or arrays // recursive call is called here and will eventually go to the following else if branch // jQuery. isPlainObject is used to test whether the Object is a pure Object. // a pure Object refers to the one created through "{}" or "new Object // If it is a deep copy if (deep & copy & (jQuery. isPlainObject (copy) | (copyIsArray = jQuery. isArray (copy) {// array if (copyIsArray) {copyIsArray = false; clone = src & jQuery. isArray (src )? Src: []; // object} else {clone = src & jQuery. isPlainObject (src )? Src: {};}// Never move original objects, clone them // recursive target [name] = jQuery. extend (deep, clone, copy); // Don't bring in undefined values // eventually overwrite the else if (copy! = Undefined) {target [name] = copy ;}}// Return the modified object // Return the new target // If I <length, is to directly return the target that has not been processed, that is, arguments [0] // that is, if the source to be overwritten is not passed, call $. extend is actually adding jQuery's static method return target ;};

It should be noted that this sentence is jQuery. extend = jQuery. fn. extend = function () {}, that is, jQuery. extend implementation and jQuery. fn. the implementation of extend shares the same method, but why can different functions be implemented? This is due to the powerful Javascript (weird ?) .

1) In jQuery. extend (), this points to a jQuery object (or a jQuery class), so it is extended here on jQuery;

2) In jQuery. fn. in extend (), this points to the fn object. jQuery is mentioned earlier. fn = jQuery. prototype, that is, the prototype method is added here, that is, the object method.


JQuery's chain call and backtracking

Another reason for everyone to like jQuery is its chain call. this implementation is actually very simple. You only need to return this in the returned results of the method to implement chain call, you can implement the chain call.

Of course, in addition to chain calls, jQuery even allows backtracking. Let's see:

// Use the end () method to terminate the latest filtering operation on the current chain and return the previous object set $ ('div '). eq (0 ). show (). end (). eq (1 ). hide ();

When ('div ') is selected '). after eq (0), end () can be used to trace back to the selected jQuery object $ ('div ') in the previous step. Its internal implementation actually relies on the added prevObject attribute:

JQuery's complete chain call, increment stack, and rollback are implemented through return this, return this. pushStack (), return this. prevObject. Let's look at the source code implementation:

JQuery. fn = jQuery. prototype = {// Add a DOM element set to the jQuery stack // This method is frequently used in jQuery DOM operations, such as in parent (), find (), the filter () // pushStack () method traces the DOM result set returned by the previous method in a chain call by changing the prevObject attribute of a jQuery object. // when we call end () in a chain () method, the prevObject attribute pushStack: function (elems) of the current jQuery object is returned internally {// construct a new jQuery object without the parameter this. constructor (), but returns reference this // jQuery. merge merges elems nodes into the new jQuery object // this. constructor is jQuery. The constructor jQuery. fn. init, so this. constructor () returns a jQuery object // due to jQuery. the object returned by the merge function is the second function appended to the first one, so ret is also a jQuery object, here we can explain why DOM objects in and out of pushStack can also be operated using the CSS method var ret = jQuery. merge (this. constructor (), elems); // Add the attribute prevObject to the returned new jQuery object. // That is why prevObject can be used to reference ret in the previous collection. prevObject = this; ret. context = this. context; // Return the newly-formed element setreturn ret;}, // the previous object of the trace chain call End: function () {// The Key To Backtracking is to return the prevObject attribute // while the prevObject attribute stores the jQuery object set return this. prevObject | this. constructor (null) ;}, // obtain the I eq of the current jQuery object: function (I) {// var len = this. length, j = + I + (I <0? Len: 0); // return this. pushStack (j> = 0 & j <len? [This [j]: []) ;},}

In general,

1) The end () method returns the prevObject attribute, which records the jQuery object collection in the previous operation;

2) The prevObject attribute is generated by the pushStack () method. This method adds a DOM element set to a stack internally managed by jQuery, you can change the prevObject attribute of a jQuery object to track the DOM result set returned by the previous method in a chained call.

3) after the end () method is called in a chain, the prevObject attribute of the current jQuery object is returned internally to complete backtracking.


Regular Expression and detail Optimization

I have to mention jQuery to do a good job in detail optimization. There are also many tips worth learning. The next article will take some programming skills in jQuery as the topic and I will not go into detail here.

Then I want to talk about regular expressions. jQuery uses a large number of regular expressions. I think the regular expression level can be greatly improved if I study jQuery, before reading this document, we recommend that you know the following:

1) understand and try to use Javascript Regular Expression related APIs, including test (), replace (), match (), exec () usage;

2) differentiate the above four methods, which is the RegExp object method and the String object method;

3) understand simple assertion with zero width, and understand what is a match but not capture or match and capture.


Variable conflict Processing

Finally, I would like to mention how to handle jQuery variable conflicts by saving window. jQuery and windw. $ of global variables at the beginning.

When a conflict needs to be handled, call the static method noConflict () to give control of the variable. The source code is as follows:

(Function (window, undefined) {var // Map over jQuery in case of overwrite // sets the alias. Two private variables Map jQuery and $ objects in the window environment, to prevent variables from being forcibly overwritten _ jQuery = window. jQuery, _ $ = window. $; jQuery. the extend ({// noConflict () method gives jQuery control of variable $, in this way, other scripts can use it. // Replace the short name with the full name to use jQuery // deep -- Boolean value, indicates whether jQuery variables can be completely restored (whether to hand over $ references and the jQuery object itself) noConflict: function (deep) {// determine whether the global $ variable is equal to the jQuery variable. // if the global variable is equal to the jQuery variable, restore the global variable $ as the variable before jQuery runs (stored in the Internal Variable _ $) if (window. $ === jQuery) {// jQuery alias $ Invalid window. $ =_$;} // when deep conflict processing is enabled and the global variable jQuery is equal to the internal jQuery, The Global jQuery is restored to the previous state if (deep & window. jQuery === jQuery) {// If deep is true, jQuery fails to use window. jQuery = _ jQuery;} // The returned jQuery Constructor (new jQuery. fn. init () // use it as much as $. return jQuery ;}} (window)

Draw a simple flowchart to help you understand:

So after the two symbols are given, can jQuery or $ be used in our code? Don't panic, you can still use:

// Giving control of jQuery and $ does not mean that jQuery and $ cannot be used. The method is as follows: var query = jQuery. noConflict (true); (function ($) {// plug-in or other forms of code, you can also set the parameter to jQuery}) (query );//... code for other libraries that use $ as an alias



The next article will analyze some of jQuery's optimization tips and some improvements to programming.

The original article is limited in writing, so it is easy to learn. If there is anything wrong with the article, please let us know.

If this article is helpful to you, click here for recommendations. It is not easy to write articles.

Finally, I will provide a full-text annotation on jQuery source code on github. If you are interested, you can view it and give it a star. JQuery v1.10.2 source code annotation.


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.