JQ part of the source code interpretation

Posted by standalone on Wed, 06 Oct 2021 00:32:15 +0200

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

  1. Run on browser side

  1. 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

  1. 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]

  1. "false/0/NaN/""/null/undefined" is passed: an empty jQuery object is returned

  2. 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 );
            }
  1. 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;

  1. 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 );
  1. 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

Topics: Javascript JQuery