preface

Hi, I’m Wakawa. This is the first article to learn the overall structure of the source code. Overall architecture this word seems to be a bit big, let’s say it is the overall structure of the source code, the main is to learn the overall structure of the code, do not go into other is not the main line of the specific function implementation. This article is about code packaged and integrated, not code broken down in the actual repository.

Learn the overall architecture of the source code series articles as follows:

1. Learn the overall architecture of jQuery source code, and create your own JS class library 2. Learn to use the whole framework of the source code to create their own functional programming library 3. Learning loDASH source code overall architecture, to create their own functional programming class library 4. Learn sentry source code overall architecture, build their own front-end exception monitoring SDK 5. Learn vuEX source code overall architecture, to create their own state management library 6. Learning axiOS source code overall architecture, to create their own request library 7. Learning koA source code overall architecture, a brief analysis of KOA onion model principle and CO principle 8. Learn redux source code overall architecture, in-depth understanding of Redux and its middleware principles

Interested readers can click to read.

JQuery is a popular JS library for more than 10 years, so it is necessary to learn its source code. Also can learn to create belong to own JS class library, job interview can add color many.

This article studies the v3.4.1 version. Unpkg.com source address: https://unpkg.com/[email protected]/dist/ jjquery

jQuery githubwarehouse

Self-executing anonymous functions

(function(global, factory){

})(typeof window ! = ="underfined" ? window: this, function(window, noGlobal){

});
Copy the code

The outside world can’t access the variables and functions inside, inside can access the outside variables, but it defines its own variables, will not access the outside variables. Anonymous functions wrap the code in, preventing it from colliding with other code and contaminating the global environment. Readers who are not familiar with self-executing functions can refer to this article. Execute function expressions immediately (IIFE)

In the browser environment, $and jQuery functions are finally mounted to the window, so $and jQuery can be accessed from the outside.

if ( !noGlobal ) {
 window.jQuery = window.$ = jQuery;
}
// The 'noGlobal' parameter is only used here.Copy the code

Support for multiple environments such as CommonJS and AMD specifications

Commonjs specification support

The CommonJS implementation mainly represents NodeJS

// Global is a global variable and factory is a function( function( global, factory ) {

// Use strict mode "use strict";
// The Commonjs or Commonjs-like environment if ( typeof module === "object" && typeof module.exports === "object" ) { // Return factory(global, if global.document existstrue);  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 );  }  // Pass this if window is not defined yet // The first parameter determines window, returns window if it exists, and returns this if it does not} )( typeof window ! = ="undefined" ? window : this, function( window, noGlobal ) {}); Copy the code

The AMD specification primarily represents RequireJS

if ( typeof define === "function" && define.amd ) {
 define( "jquery"[],function() {
  return jQuery;
});}
Copy the code

The CMD specification primarily stands for SEAJS

Unfortunately, support for SeaJS is not exposed in the jQuery source code. But there are also schemes online. I won’t go into specifics here. After all, seaJS are hardly used anymore.

No new structure

It’s actually new because jQuery is a function. And it has the same effect as not using new. New displays the returned object, so it has the same effect as calling jQuery directly. If you don’t know what you’re doing with the new operator. See my previous post.

Interviewer: can you emulate the JS new operator

Source:

 var
 version = "3.4.1 track.".
 // Define a local copy of jQuery
 jQuery = function( selector, context ) {
// Return the object after new return new jQuery.fn.init( selector, context );  }; jQuery.fn = jQuery.prototype = { // jQuery current version jquery: version, // Fix the constructor to jQuery constructor: jQuery,  length: 0, }; init = jQuery.fn.init = function( selector, context, root ) {  // ...  if ( !selector ) {  return this;  }  // ... }; init.prototype = jQuery.fn; Copy the code
jQuery.fn === jQuery.prototype;  // true
init = jQuery.fn.init;
init.prototype = jQuery.fn;
/ / that isjQuery.fn.init.prototype === jQuery.fn;  // true
jQuery.fn.init.prototype === jQuery.prototype; // true Copy the code

I drew a picture of thisjQueryA prototype diagram is worth a thousand words.

<sciprt src="https://unpkg.com/[email protected]/dist/jquery.js">
</script>
console.log({jQuery});
// In the Chrome console, you can see that there are many static properties and methods mounted under jQuery functions, and also on jquery.fn.Copy the code

In the Vue source code, similar to jQuery, the Vue.prototype._init method is executed.

function Vue (options) {
 if(! (this instanceof Vue) ) {
  warn('Vue is a constructor and should be called with the `new` keyword');
 }
 this._init(options); } initMixin(Vue); function initMixin (Vue) {  Vue.prototype._init = function (options) {}; }; Copy the code

One of the core functions, extend

Usage:

jQuery.extend( target [, object1 ] [, objectN ] )        Returns: Object

jQuery.extend( [deep ], target, object1 [, objectN ] )
Copy the code

jQuery.extend API jQuery.fn.extend API

Here are a few examples: (The example can be put in the online editing code of the jQuery. Extend example codepen, can be run directly).

// 1. jQuery.extend( target)
var result1 = $.extend({
 job: 'Front-end Development Engineer'.});

console.log(result1, 'result1', result1.job); // The $function adds an attribute job // Front-end development engineer // 2. jQuery.extend( target, object1) var result2 = $.extend({  name: 'if the sichuan'.}, {  job: 'Front-end Development Engineer'.});  console.log(result2, 'result2'); // { name: 'if the sichuan', job: 'Front-end Development Engineer' }  // Deep copy// 3. jQuery.extend( [deep ], target, object1 [, objectN ] ) var result3 = $.extend(true, {  name: 'if the sichuan'. other: {  mac: 0,  ubuntu: 1,  windows: 1,  }, }, {  job: 'Front-end Development Engineer'. other: {  mac: 1,  linux: 1,  windows: 0,  } }); console.log(result3, 'result3'); // deep true / / {// "name": "If sichuan".// "other": { // "mac": 1, // "ubuntu": 1, // "windows": 0.// "linux": 1.// }, // "job": "Front-end Development Engineer" // } // deep false / / {// "name": "If sichuan".// "other": { // "mac": 1, // "linux": 1, // "windows": 0 // }, // "job": "Front-end Development Engineer" // } Copy the code

Conclusion: The extend function can be implemented for both shallow and deep copies of jQuery functions. You can add static methods and properties to jQuery. You can also add properties and methods to jquery.fn (jquery.prototype). This functionality is attributed to this. JQuery. Fn. Extend this refers to jquery.fn.

Shallow copy implementation

Knowing this, shallow copying is actually relatively easy to implement:

// Shallow copy implementationjQuery.extend = function() {Object1, object2... var options,
// Object Specifies the key on the object name, // Copy object specifies the value of the object to be copied copy, // The extension target object may not be an object, so or an empty object target = arguments[0] || {}, // Define I as 1 i = 1, // Define the number of arguments length = arguments.length; // When there is only one parameter if(i === length){  target = this;  i--;  }  for(; i < length; i++){ // I pay, and I am not null if((options = arguments[i]) ! = null){ for(name in options){  copy = options[name]; // Prevent an infinite loop,continueJump out of the current loop if ( name === "__proto__" || target === copy ) {  continue;  }  if( copy ! == undefined ) { target[ name ] = copy;  }  }  }   } // Finally return the target object return target; } Copy the code

The deep copy is mostly made in the following code. It could be an array or an object that references a value of a type to make a judgment.

if( copy ! == undefined ) { target[ name ] = copy;
}
Copy the code

In order to facilitate reader debugging, the code is also in the jQuery. Extend shallow copy code codepen, can run online.

Deep copy implementation

$.extend = function() {Object1, object2... var options,
// Object Specifies the key on the object name,
// Copy object specifies the value of the object to be copied copy, Deep, SRC, copyIsArray,clone  deep = false.// Source target, which needs to be assigned to src, // The type of the value to be copied is function copyIsArray,  //  clone.// The extension target object may not be an object, so or an empty object target = arguments[0] || {}, // Define I as 1 i = 1, // Define the number of arguments length = arguments.length;  // Handle the deep copy situation if ( typeof target === "boolean" ) {  deep = target;   // Skip the boolean and the target // target The target object starts to move back target = arguments[ i ] || {};  i++;  }   // Handle case when target is a string or something (possible in deep copy) // If target is not equal to an object, and target is not a function, force it to be an empty object. if( typeof target ! = ="object" && !isFunction( target ) ) {  target = {};  }  // When there is only one parameter if(i === length){  target = this;  i--;  }  for(; i < length; i++){ // I pay, and I am not null if((options = arguments[i]) ! = null){ for(name in options){  copy = options[name]; // Prevent an infinite loop,continueJump out of the current loop if ( name === "__proto__" || target === copy ) {  continue;  }   // Recurse if we're merging plain objects or arrays // Where deep is true and the value to be copied has a value and is a pure object// Or the value to be copied is an array if ( deep && copy && ( jQuery.isPlainObject( copy ) ||  ( copyIsArray = Array.isArray( copy ) ) ) ) {  // Source target, which needs to be assigned to src = target[ name ];   // Ensure proper type for the source value SRC is not an array, and the clone object is an empty array.if ( copyIsArray && ! Array.isArray( src ) ) { clone = []; // The copied values are not arrays, and the objects are not pure objects.} else if ( ! copyIsArray && ! jQuery.isPlainObject( src ) ) {// Assign an empty object to clone clone = {};  } else { // Otherwise, clone = SRC clone = src;  } // On the next loop, copyIsArray needs to be reassigned to false copyIsArray = false;   // Never move original objects, clone them // Call yourself recursively target[ name ] = jQuery.extend( deep, clone, copy );   // Don't bring in undefined values  }  else if( copy ! == undefined ) { target[ name ] = copy;  }  }  }   } // Finally return the target object return target; }; Copy the code

For reader debugging purposes, this code is also implemented in the jQuery. Extend deep copy code codepen, which can be run online.

Deep copy derived function isFunction

Determine whether the parameter is a function.

var isFunction = function isFunction( obj ) {

 // Support: Chrome <=57, Firefox <=52
 // In some browsers, typeof returns "function" for HTML <object> elements
 // (i.e., `typeof document.createElement( "object") = = ="function"`).
 // We don't want to classify *any* DOM node as a function. return typeof obj === "function" && typeof obj.nodeType ! == "number";}; Copy the code

Deep copy derived function jquery.isPlainObject

Jquery.isplainobject (obj) Tests whether the Object is a pure Object(created with “{}” or “new Object”).

jQuery.isPlainObject({}) // true
jQuery.isPlainObject("test") / /false
Copy the code
var getProto = Object.getPrototypeOf;
var class2type = {};
var toString = class2type.toString;
var hasOwn = class2type.hasOwnProperty;
var fnToString = hasOwn.toString;
var ObjectFunctionString = fnToString.call( Object );  jQuery.extend( {  isPlainObject: function( obj ) {  var proto, Ctor;   // Detect obvious negatives  // Use toString instead of jQuery.type to catch host objects / /! Obj fortrueOr not [object object]// Return directlyfalse  if(! obj || toString.call( obj ) ! = ="[object Object]" ) {  return false;  }   proto = getProto( obj );   // Objects with no prototype (e.g., `Object.create( null )`) are plain // The stereotype does not exist. For example, object.create (null) returns directlytrue;  if ( !proto ) {  return true;  }   // Objects with prototype are plain iff they were constructed by a global Object function  Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; // The constructor is a function, and fntoString.call (Ctor) === FntoString.call (Object); return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;  }, }); Copy the code

Extend (); extend (); extend (); And the use of a wide range of internal can also be used, external use of extension plug-ins and so on.

Chain calls

JQuery enables chained calls because some functions return this after execution. For example, addClass, removeClass, toggleClass in jQuery source code.

jQuery.fn.extend({
 addClass: function() {  // ...
  return this;
 },
 removeClass: function() { // ...  return this;  },  toggleClass: function() { // ...  return this;  }, }); Copy the code

jQuery.noConflictA lot ofjsLibraries have anti-collision functions

jQuery.noConflict API

Usage:

 <script>
 var $ = 'I'm the other $, jQuery don't overwrite me';
</script>
<script src=". / jquery - 3.4.1 track. Js. "">
</script>
<script>  $.noConflict(); console.log($); // I am the other $, jQuery do not overwrite me</script> Copy the code

JQuery. Of noConflict source code

var

 // Map over jQuery in case of overwrite
 _jQuery = window.jQuery,

 // Map over the $ in case of overwrite  _$ = window.$;  jQuery.noConflict = function( deep ) { $=== jQuery;// assign the existing _$value to window.$; if ( window.$ === jQuery ) {  window.$ = _$;  }  // If deep istrueJQuery === jQuery;// Assign the existing _jQuery value to window.jquery; if ( deep && window.jQuery === jQuery ) {  window.jQuery = _jQuery;  }  // Return jQuery return jQuery; }; Copy the code

conclusion

This paper mainly analyzes the overall structure of jQuery, including self-executing anonymous functions, no new constructs, support for a variety of specifications (such as CommonJS and AMD specifications), extend of core functions, chained calls, and jquery.noconflict, etc.

Reorganize the structure of the source code learned below.

// The source code structure( 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( window, noGlobal ) {  var version = "3.4.1 track.".  // Define a local copy of jQuery  jQuery = function( selector, context ) {  return new jQuery.fn.init( selector, context );  };   jQuery.fn = jQuery.prototype = {  jquery: version,  constructor: jQuery,  length: 0,  // ...  };   jQuery.extend = jQuery.fn.extend = function() {};   jQuery.extend( {  // ...  isPlainObject: function( obj ) {},  // ...  });   init = jQuery.fn.init = function( selector, context, root ) {};   init.prototype = jQuery.fn;   if ( typeof define === "function" && define.amd ) {  define( "jquery"[],function() {  return jQuery; }); }  jQuery.noConflict = function( deep ) {};   if ( !noGlobal ) {  window.jQuery = window.$ = jQuery;  }   return jQuery; }); Copy the code

You can learn the clever design and architecture of jQuery, and create your own JS class library for your own use. Related code and resources are posted on the Github blog and are available to readers who need them.

The next article is to learn the overall source architecture of Underscorejs. Learn to underscore the overall framework of source code, to create their own functional programming library

Readers are welcome to comment on any problems or improvements they find. In addition, I think it is well written. I can like, comment and forward it, which is also a kind of support for the author.

The author’s previous article

Q: Can you simulate the JS call and apply method? Q: Can you simulate the JS bind method? Can simulate the implementation of the JS new operator front end using puppeteer crawler to generate the React. JS small book PDF and merge

Further reading

Chokcoco: songjz :jQuery source code series (1) overall architecture

Another series by the author

How can you simulate the JS call and apply methods? How can you simulate the bind method? How can you simulate the new operator

about

Author: often with the name of Ruochuan mixed in rivers and lakes. The front road lovers | | PPT know little, only good study. Ruakawa’s blog has been reconstructed using vuepress, and the reading experience may be better. Welcome to the segmentfault front view column, welcome to the whisperer front view column, welcome to the new whisperer column, welcome to the zhihu front view column, welcome to the github blog, Related source code and resources are put here, ask for star^_^~

Welcome to add wechat communication wechat public account

May be more interesting wechat public number, long press scan code concern. Welcome to add the author’s wechat ruochuan12 (specify the source, basic users do not refuse), pull you into [front view communication group], long-term communication and learning ~

If sichuan vision

This article was typeset using MDNICE