preface
Why read jQ source code. Read jquery source
Code can learn the programming ideas inside and see how jq encapsulates some of our common methods. On the one hand, these core ideas can consolidate our js foundation and use ability, on the other hand, they can write some encapsulation and underlying construction for us, which are good references.
1, Environmental treatment analysis
1-1 in order to prevent the code variables inside from polluting the external, closures are used
(function(global,factory){ })(typeof window !== undefined ? window :this ,function(window,noGlobal){ })
This is the outermost structure
If you continue to analyze it, you can see that you can judge the operation of different environments
To understand this paragraph, you need to analyze the js running environment
1-2 js running environment
-
Browser webkit(blink), gecko, trident
-
The webkit in the webview "mobile APP" has a window, does not support the CommonJS specification, and supports the ES6Module specification
-
The node environment does not have a window, supports the CommonJS specification, but does not support the ES6Module specification
-
It can be compiled based on webpack
Support window, CommonJS specification and ES6Module specification (mixed calls of ES6Module and CommonJS can be made)...: package based on node environment, and submit the packaged results to the browser for rendering and running
Then we analyze this adaptive function
(function(global,factory){ //Run JQ on the node side: Global - > this //The webpack side runs jq: Global - > window //Run jq: global -window on the browser side "use strict" if(typeof module === "object" && typeof module.exports === "object"){ //Support commonjs module specification node // webpack module.exports = global.document ? // Global - > window runs at the lower end of the webpack and exports the returned results of factory execution as modules factory( global, true ) : // Global - > this runs on the node side and directly exports a function function( w ) { if ( !w.document ) { throw new Error( "jQuery requires a window with a document" ); } return factory( w ); }; }else{ // Running jq on the browser side will execute the factory function and pass the argument window factory(global) } })(typeof window !== undefined ? window :this ,function(window,noGlobal){ // Run window - > window on the browser side // Run NoGlobal - > undefined on the browser side // Run window - > Global on the webpack // Run NoGlobal - > true in webpack "use strict"; var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); }; if ( typeof noGlobal === "undefined" ) { window.jQuery = window.$ = jQuery; } return jQuery; })
Look at the internal input parameters of this method. One is window (the specific value varies according to different environment values) and the other is method
After reading the internal judgment executed by this anonymous function, you need to know something to understand this interpretation:
Run JQ on the node side: Global - > this
Run jQ on the webpack side: Global - > window
Run jQ: global -window on the browser side
Node / / supports commonjs module specification under webpack. Browser does not support
In this way, it can be divided into two categories. Those that meet the commonjs specification are divided into one category (node // webpack)
typeof module === "object" && typeof module.exports === "object"
Another type of dissatisfaction is the browser environment. Running jq on the browser side will execute the factory function and pass the actual parameter window,
Run window - > window and NoGlobal - > undefined on the browser side
Run window - > Global NoGlobal - > true on the webpack
1-3. Operate in different environments jq
- Run on browser side
- Run on the node side
/* CommonJS Module management specification module.exports Export module require Import module */ let $ = require('jquery') console.log($) console.dir($) console.dir($.toString()) console.log($.toString()) $()
Print out in sequence:
It is proved that the node side cannot run jq directly
- Run in webpack
vue create testjquery create a new vue project
It can be seen that $() can be executed. The webpack environment can execute jQ
1-4 encapsulate your own class library (distinction between environment variables)
After learning the idea of jQ, we will write a class library of methods, and then we can apply the compatibility judgment of this environment to our own projects, as shown in the following code:
// Just want to run on the webpack and browser side, and an error is reported on the NODE side (function (global, factory) { "use strict"; if (typeof module === "object" && typeof module.exports === "object") { module.exports = global.document ? factory(global, true) : function (w) { if (!w.document) { throw new Error("jQuery requires a window with a document"); } return factory(w); }; } else { factory(global); } })(typeof window !== "undefined" ? window : this, function factory(window, noGlobal) { let utils = {}; /!* Export module *!/ if (typeof define === "function" && define.amd) { define("utils", [], function () { return utils; }); } if (typeof noGlobal === "undefined") { window.utils = utils; } return utils; });
2, Execute $() -- Application of factory design pattern
2-1 JQ selector
JQ selector: $([selector]) the latter jQuery ([selector]) = > executes the private jQuery method as an ordinary function
- new jQuery.fn.init/init(selector, context) is created in the instance "@ A" of init class
- @A.proto=init.prototype and init.prototype=jQuery.prototype
- @A.proto===jQuery.prototype
- Description the result obtained by JQ selector is the instance object of jQuery class
Interview question: if you execute a method as an ordinary function, can you get the instance object of the current constructor?
Plant design mode:
2-2JQ object, DOM element object
JQ object: the instance object of JQ "can call properties and methods on jQuery.fn, but the browser built-in methods cannot be called"
It is a collection of class arrays, and each item in the collection stores DOM element objects
DOM element object: Based on the built-in properties and methods of JS, the obtained DOM object "is not a JQ instance. You cannot call the properties and methods on jQuery.fn, but you can call the built-in methods of the browser"
2-3. Various situations of [selector]
-
"false/0/NaN/""/null/undefined" is passed: an empty jQuery object is returned
-
String passed:
Newly created HTML string: dynamically create the corresponding DOM element according to the HTML string, and finally insert it into the page
The style selector string is used to get the specified DOM element
if ( selector[ 0 ] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; } else { match = rquickExpr.exec( selector ); } // Match html or make sure no context is specified for #id if ( match && ( match[ 1 ] || !context ) ) { // HANDLE: $(html) -> $(array) if ( match[ 1 ] ) { context = context instanceof jQuery ? context[ 0 ] : context; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge( this, jQuery.parseHTML( match[ 1 ], context && context.nodeType ? context.ownerDocument || context : document, true ) ); // HANDLE: $(html, props) if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { // Properties of context are called as methods if possible if ( isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); // ...and otherwise set as attributes } else { this.attr( match, context[ match ] ); } } } return this; // HANDLE: $(#id) } else { elem = document.getElementById( match[ 2 ] ); if ( elem ) { // Inject the element directly into the jQuery object this[ 0 ] = elem; this.length = 1; } return this; } // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { return ( context || root ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); }
- The DOM element object is passed: the DOM element object is changed into a JQ instance object. The purpose is to use the methods provided by JQ or the browser
else if ( selector.nodeType ) { this[ 0 ] = selector; this.length = 1; return this;
- The function passed is $(function() {}) < = > $(document). Ready (function() {}) listens to DOMContentLoaded event "and listens based on DOM2 event binding", so it will wait for the callback function to execute as soon as the page structure is loaded (DOM TREE is generated)!! And the same page can be used multiple times!!
return root.ready !== undefined ? root.ready( selector ) : // Execute immediately if ready is not present selector( jQuery ); } return jQuery.makeArray( selector, this );
- Other values are passed
Array or class array: merge the passed array and class array with the JQ instance object, and finally return the JQ instance object
Not an array or class array: add the passed value directly to the end of the JQ instance
return jQuery.makeArray( selector, this );
This involves several methods of merging arrays and class arrays
// results is for internal usage only jQuery.makeArray= function( arr, results ) { var ret = results || []; if ( arr != null ) { if ( isArrayLike( Object( arr ) ) ) { jQuery.merge( ret, typeof arr === "string" ? [ arr ] : arr ); } else { push.call( ret, arr ); } } return ret; }, //Determine whether it is a class array // results is for internal usage only function isArrayLike( obj ) { // Support: real iOS 8.2 only (not reproducible in simulator) // `in` check used to prevent JIT error (gh-2145) // hasOwn isn't used here due to false negatives // regarding Nodelist length in IE var length = !!obj && "length" in obj && obj.length, type = toType( obj ); if ( isFunction( obj ) || isWindow( obj ) ) { return false; } return type === "array" || length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj; } //Merge arrays or class arrays // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge= function( first, second ) { var len = +second.length, j = 0, i = first.length; for ( ; j < len; j++ ) { first[ i++ ] = second[ j ]; } first.length = i; return first; },
No matter what is passed, the JQ instance object is returned at last