[Simple introduction to jQuery] source code analysis 2-great tricks, simple introduction to jquery2 --

Source: Internet
Author: User
Tags hasownproperty

[Simple introduction to jQuery] source code analysis 2-great tricks, simple introduction to jquery2 --

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.


This is the second article in the series. The title is a little big, and I hope the content can be up to this title. This article mainly summarizes some of the very clever coding methods in jQuery, which will be from a very small to a deep perspective, there may be some basics, but I hope it will be more comprehensive and helpful to anyone reading the article. I have been reading the source code and will keep updating this article.

Even if you don't want to read the source code and look at the summary below, I think it will be of great benefit to improve programming capabilities and change the way of thinking.


Short-circuit expression and multiple short-circuit expression

The short-circuit expression should be known to all. In jQuery, short-circuit expressions and multiple short-circuit expressions are widely used.

Short-circuit expression: Used as the operand expressions of the "&" and "|" operators. When evaluating these expressions, as long as the final result is determined to be true or false, the process of evaluation is terminated, which is called short-circuit evaluation. This is an important attribute of these two operators.

// | Short circuit expression var foo = a | B; // equivalent to if (a) {foo = a;} else {foo = B ;} // & Short Circuit expression var bar = a & B; // equivalent to if (a) {bar = B;} else {bar = ;}

Of course, the above two examples show the simplest short-circuit expressions. In most cases, jQuery uses them like this:

// Function siblingCheck (a, B) {var cur = B & a, diff = cur &. nodeType = 1 & B. nodeType = 1 &&(~ B. sourceIndex | MAX_NEGATIVE )-(~ A. sourceIndex | MAX_NEGATIVE); // other code ...}

Well, we can see that the diff value is obtained through multiple short-circuit expressions combined with some full-length judgments. This type of code is very elegant, but the readability is much reduced. We should weigh it when using it, the multiple short-circuit expressions are the same as the simple short-circuit expressions. You only need to take the subsequent expressions as a whole and push them forward in order to obtain the final value.

Var a = 1, B = 0, c = 3; var foo = a & B & c, // 0, equivalent to a & (B & c) bar = a | B | c; // 1

Note the following points:

1. In the logic operation of Javascript, 0, "", null, false, undefined, and NaN are all regarded as false, while others are all true;

2. Because of the built-in weak type domain (weak-typing domain) of Javascript, I am not very concerned about strict input verification, even if the number of operations using the & or | Operator is not a Boolean value, it can still be regarded as a Boolean operation. However, we recommend the following:

If (foo) {...} // not rigorous enough if (!! Foo) {...} // more rigorous ,!! Other types of values can be converted to the boolean type.

Focus on the details. JavaScript is neither weak nor low. We just need to work harder to make our code truly robust.


Entry to pre-define common methods

In jQuery's first dozens of lines, there is such an interesting piece of code:

(Function (window, undefined) {var // defines an object variable, a string variable, an array variable class2type ={}, core_version = "1.10.2", core_deletedIds = [], // some common methods for saving objects, strings, and arrays, such as concat push... core_concat = core_deletedIds.concat, core_push = core_deletedIds.push, core_slice = core_deletedIds.slice, core_indexOf = core_deletedIds.indexOf, core_toString = class2type. toString, core_hasOwn = class2type. hasOwnProperty, core_trim = core_version.trim;}) (window );

I have to say that jQuery is doing very well in detail. Here we first define an object variable, a string variable, and an array variable, note that these three variables have their own purposes in the following sections (we can see that jQuery's word is like gold, which is really used to squeeze every variable to maximize its role ); second, we use these three variables to define some common core methods, from top to bottom are the concat, push, slice, indexOf methods of the array, the toString, hasOwnProperty, and trim methods of the string, core_xxxx variables of the object store the portals of these commonly used methods in advance. If you need to call these methods in the following text, it will:

JQuery. fn = jQuery. prototype = {//... // convert the jQuery object to the array type toArray: function () {// call the slice Method of the array and use the pre-defined core_slice to save time for searching the memory address, improve Efficiency // equivalent to return Array. prototype. slice. call (this) return core_slice.call (this );}}

We can see that when you need to use these pre-defined methods, you only need to call them by using call or apply.

So why does jQuery do this? I think:

1. Take the concat method of the Array object as an example. If you do not define core_concat = core_deletedIds.concat in advance but call the concat method of the Instance arr, you must first identify that the arr type of the current instance is Array, find the concat memory entry of the Array in the memory space, push the pointer and other parameters of the current object arr to the stack, jump to the concat address, and start execution. When the concat method entry core_concat is saved, the preceding two steps can be omitted to improve the performance;

2. In addition, the call or apply method allows some class arrays to directly call the array method. As shown in the preceding example, the jQuery object is of the class array type. You can directly call the slice Method of the array to convert it to the array type. For example, convert the parameter arguments to the array type:

Function test (a, B, c) {// convert the arguments parameter to an Array // you can call the Array member method var arr = Array. prototype. slice. call (arguments );...}



In versions earlier than jQuery 2.0.0, a lot of compatibility is processed, so that developers can ignore the different features of different browsers and focus on the business logic. Among them, the hook mechanism plays a huge role in browser compatibility.

Hooks are a common programming method used to solve one or more special situations.

In simple terms, hooks are the adapter principle, or the table-driven principle. We have defined some hooks in advance and used hooks in the normal code logic to adapt to some special attributes, styles, or events, this allows us to write a lot less else if statements.

If it is still difficult to understand, let's look at a simple example to illustrate how to use the hook:

Currently, civil servants can either rely on strength or relationship, but the leaders will certainly not make it so obvious. Generally, it is a dark box operation. This scenario is more reasonable to use hooks.

// If no hook is needed // score of the examinee and function examinee (name, score, fatherName) {return {name: name, score: score, fatherName: fatherName };} // review the examinee function judge (examinees) {var result ={}; for (var I in examinees) {var curExaminee = examinees [I]; var ret = curExaminee. score; // determine whether there is a backdoor relationship if (curExaminee. fatherName = 'xijingping') {ret + = 1000;} else if (curExaminee. fatherName = 'shanghai') {ret + = 100;} else if (curExaminee. fatherName = 'pengdehuai') {ret + = 50;} result [curExaminee. name] = ret;} return result;} var lihao = examinee ("lihao", 10, 'libang '); var xida = examinee ('xida', 8, 'xijinping'); var peng = examinee ('peng', 60, 'pengdehuai'); var liaoxiaofeng = examinee ('liaoxiaofeng ', 100, 'liaodaniu '); var result = judge ([lihao, xida, peng, liaoxiaofeng]); // select the top three for (var name in result) {console. log ("name:" + name); console. log ("score:" + score );}

We can see that in the intermediate review of the examinee function, many else if statements are used to determine whether the examinee has a backdoor relationship. if the current business scenario changes, several more candidates are added, therefore, else if is bound to become more and more complex, and future code maintenance will become more and more troublesome and costly. What should we do if the hook mechanism is used in this case?

// RelationHook is a hook function used to obtain the link score var relationHook = {"xijinping": 1000, "ligang": 100, "pengdehuai": 50, // new examinee only needs to add link score} // examinee score and father name function examinee (name, score, fatherName) {return {name: name, score: score, fatherName: fatherName};} // review the candidate function judge (examinees) {var result = {}; for (var I in examinees) {var curExaminee = examinees [I]; var ret = curExaminee. score; if (relationHook [curExaminee. fatherName]) {ret + = relationHook [curExaminee. fatherName];} result [curExaminee. name] = ret;} return result;} var lihao = examinee ("lihao", 10, 'libang '); var xida = examinee ('xida', 8, 'xijinping'); var peng = examinee ('peng', 60, 'pengdehuai'); var liaoxiaofeng = examinee ('liaoxiaofeng ', 100, 'liaodaniu '); var result = judge ([lihao, xida, peng, liaoxiaofeng]); // select the top three for (var name in result) {console. log ("name:" + name); console. log ("score:" + score );}

You can see that using hooks to handle special cases can make the code logic clearer and save a lot of conditional judgment. The implementation method of the hook mechanism above adopts the table-driven approach, that is, we have prepared a table (also known as a table) in advance and used this table to adapt to special situations. Of course, jQuery's hook is a more abstract concept that can be implemented in different scenarios in different ways.

Look at the table-driven hook implementation in jQuery, $. type method:

(Function (window, undefined) {var // used to pre-store a type table for hookclass2type = {}; // The native typeof method cannot differentiate a variable. It is an object type such as Array and RegExp. jQuery has $ to extend the expressiveness of typeof. type method // for some special objects (such as null, Array, RegExp), precise type judgment is also carried out. // The Hook mechanism is used to create tables of common types before determining the type, first saved in a Hash table class2type. each ("Boolean Number String Function Array Date RegExp Object Error ". split (""), function (I, name) {class2type ["[object" + name + "]"] = Name. toLowerCase () ;}); jQuery. extend ({// determine the JavaScript Object Type // The Key to this method lies in class2type [core_toString.call (obj)] // The typeof obj is of the "object" type to further accurately determine the type: function (obj) {if (obj = null) {return String (obj );} // use the pre-stored hash table class2type for precise judgment // This is because of the existence of the hook, saves a lot of else if to judge return typeof obj = "object" | typeof obj = "function "? Class2type [core_toString.call (obj)] | "object": typeof obj ;}}) (window );

The hook here is only the tip of the iceberg where jQuery uses a large number of hooks. It is used in operations on DOM elements. The attr, val, prop, and css methods use a large number of hooks, compatible with some weird behaviors in the IE series. When encountering a hook function, you need to analyze the specific situation. These hooks are more complex than table drivers. Their structure is roughly as follows, as long as you remember the core principles of the hook, keep the overall logic of the Code smooth and handle some special situations in special situations:

var someHook = {    get: function(elem) {        // obtain and return a value        return "something";    },    set: function(elem, value) {        // do something with value    }}

To some extent, hooks are a series of callback functions designed to process custom values with your own code. With hooks, you can keep almost everything under control.


Coherent Interface

Whether or not jQuery's current popularity is declining, it is truly appealing to use, thanks to its chain call, interface consistency and easy to remember. Many people think of a coherent interface as a chain call, which is not comprehensive. I think a coherent interface includes a chain call and represents more. JQuery is undoubtedly a leader in coherent interfaces.

1. chained call: the main idea of chained call is to make the code as fluent and easy to read as possible, so that it can be understood more quickly. With chained calling, we can organize code into fragments of similar statements to enhance readability while reducing interference. (The specific implementation of chained call is described in the previous chapter)

// Traditional method var elem = document. getElementById ("foobar"); elem. style. background = "red"; elem. style. color = "green"; elem. addEventListener ('click', function (event) {alert ("hello world! ") ;}, True); // jQuery syntax ('xxx'background .css (" background "," red "background .css (" color "," green "). on ("click", function (event) {alert ("hello world ");});

2. Command query is the same as that in the previous chapter, that is, function overloading. Normally, it should be Command and Query Separation (CQS), which is a concept derived from imperative programming. Functions that change the object state (internal values) are called commands, while those that retrieve values are called queries. In principle, the query function returns data. The command function returns the status and performs its duties. JQuery compresses the getter and setter methods into a single method and creates a coherent interface, so that fewer methods are exposed, but less code is used to achieve the same goal.

3. Parameter ing and processing: jQuery's interface consistency is also reflected in the compatibility processing of parameters. It is more important to receive data in a method than to make them chained. Although method chain calls are very common, you can easily implement them in your code, but the processing parameters are different. Users may input various strange parameter types, the author of jQuery is really considerate. Considering a variety of user scenarios, it provides a variety of parameter processing methods.

// Input the key-Value Pair jQuery ("# some-selector "). css ("background", "red "). css ("color", "white "). css ("font-weight", "bold "). css ("padding", 10); // input the JSON object jQuery ("# some-selector" ).css ({"background": "red", "color ": "white", "font-weight": "bold", "padding": 10 });

The on () method of jQuery can register an event processor. Like CSS (), it can also receive events in a set of ing formats, but further, it allows a single processor to be registered by multiple events:

// binding events by passing a mapjQuery("#some-selector").on({  "click" : myClickHandler,  "keyup" : myKeyupHandler,  "change" : myChangeHandler});// binding a handler to multiple events:jQuery("#some-selector").on("click keyup change", myEventHandler);


No new construction

How can I access the attributes and methods on the jQuery prototype, and how can I isolate the scope and use the scope of the jQuery prototype object? The point is that:

// Give the init function the jQuery prototype for later instantiationjQuery.fn.init.prototype = jQuery.fn;

The key here is to solve the problem through prototype transfer. This part is also discussed in the previous chapter. You can skip it and move the text.

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.



Here, I found that the above theme is somewhat erratic, close to how to write better Javascript code. Below I will introduce some tips that I think are great in jQuery.

Anyone familiar with jQuery knows about the DOM Ready event. Passing the Javascript native window. onload event is triggered after all the resources on the page are loaded. If the response of resources such as large images on the page is slow, the window. onload event will be delayed, so the DOM Ready event will occur. This event is triggered after the DOM document structure is prepared, that is, before the resource is loaded. In addition, we need to modify the DOM structure after DOM preparation, such as adding DOM elements. To perfectly implement DOM Ready events, compatible with browsers and earlier versions of IE (for advanced browsers, DOMContentLoaded events can be used to save time and effort. the ready () method uses a feature of the setTimeout () method. The function triggered in setTimeout must be triggered after DOM preparation.

JQuery. extend ({ready: function (wait) {// if you need to wait, when holdReady (), reduce the number of hold times by 1. If it hasn't reached 0, it means you still need to hold it and return it. // if you do not need to wait, you can determine whether Ready has passed. If ready has passed, you do not need to handle it. If (wait = true? -- JQuery. readyWait: jQuery. isReady) {return;} // determine whether the body exists if (! Document. body) {// if the body does not exist, DOMContentLoaded is not completed. // set jQuery. put ready in the setTimeout timer // setTimeout (a) without the time parameter is equivalent to setTimeout (a, 0) // But jQuery is not triggered immediately here. ready // the asynchronous mode of a javascript single thread // setTimeout (jQuery. ready) will wait until the re-painting is complete before the code is executed, that is, after DOMContentLoaded. ready // so here is a tips: The function triggered in setTimeout will certainly trigger return setTimeout (jQuery. ready);} // Remember that the DOM is ready // record DOM ready JQuery has been completed. isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be // false indicates that the ready event has not been triggered; otherwise, returnif (wait! = True & -- jQuery. readyWait> 0) {return;} // If there are functions bound, to execute // call the asynchronous queue, and then dispatch the successful event (finally receive it using done, switch the context to document. The first parameter by default is jQuery. ReadyList. resolveWith (document, [jQuery]); // Trigger any bound ready events // jQuery can also Trigger its own ready event // For example: // $ (document ). on ('ready', fn2); // $ (document ). ready (fn1); // fn1 will be executed first, and the fn2 callback bound to the ready event will be executed if (jQuery. fn. trigger) {jQuery (document ). trigger ("ready "). off ("ready ");}}})


For the time being, there are many other skills, such as the implementation of $. Deferred () asynchronous queues and jQuery event stream mechanism. The length is long and will be detailed later.

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.