【 Preface 】 The recent project busy feet do not touch the ground, just finished, ready to sort out some study notes and technical articles written before. This article is originally a long time ago to see the JQ source code to write fragments, after a long time to see all forget about. Just sort it out and make a note.

                

As a front end engineer, jQuery is one of the tools we are most familiar with, and its powerful functionality and near-perfect compatibility package make it a must-have skill in the front end world. I have used JQ for a long time, but I have not explored and learned jQuery source code. It seems that I am not a qualified front-end ER. Recently while taking the time to study, jQuery source code can be described as deep as the sea, mysterious infinite, usually work occasionally need to package tools, also do not need to be as comprehensive as it, but simple learning its packaging ideas or very meaningful. Take time to carefully study each piece of source code. Here’s a quick overview of my understanding of jQ framework encapsulation, modeled after two approaches to CSS encapsulation.

First, jQuery is essentially a library that encapsulates many methods

The framework of this library is a closure

Its outermost frame, or skeleton, is as follows:

(function(window, undefined) {
    var jQuery = function () {}
    window.jQuery = window.$ = jQuery;// Expose the global}) (window)Copy the code

The framework structure is obvious. In a closure (sandbox), all the functions and methods are inside the sandbox. There is a function called jQuery, which is the core function around which all the members run.

The reason why we pass window and undefined is very simple. It is needless to say that local variables are exposed as global variables. On the other hand, it is to simplify the code and reduce the variable retrieval time.

As for undefined, this is to deal with a minor problem with Internet Explorer 8. In these older browsers, undefined can be used as a variable name and reassigned, but newer browsers don’t support this anymore. Undefined is passed here to prevent undefined from being reassigned.

In this case, we can create a jQuery instance object externally:

console.log(new jQuery())// An empty object with only one __proto__ attributeCopy the code

But obviously, this object is so far from the JQ object we actually use that we can’t even see the resemblance, so take your time and take it one step at a time.

Our goal is to implement a similar approach to jQ, where a jQ object such as $(‘div’) is available externally. This jQ object is simplified and has roughly the following structure:

['div'.'div'.'div',length:3]Copy the code

This means that we have to pass in an element selector that receives, through some method, such an object in the constructor.

So let’s start with the loose assumption that jQuery is a constructor that creates objects.

In the jQuery ‘constructor’ inside the sandbox, we pass in a selector, and then we can use the constructor new to generate the object:

var jQuery = function (selector) {
    var ele = document.querySelectorAll(selector);
    Array.prototype.push.apply(this, ele);
}Copy the code

Docuemnt. QuerySelectorAll access is a “pseudo array or collection, the dom object are stored in ele above, but we need to get these objects on the jq object, so we have to manually add these objects to the instance. Using the array push method, not only can achieve the goal easily and quickly, but also the array length attribute can be automatically updated. This is a little trick.

This refers, of course, to jQuery instance objects, so we can simply add methods to the jQuery prototype and make them accessible to external instance objects, which is pretty close to jQ thinking.

From there, we simply add two methods to the prototype, CSS and HTML:

jQuery.prototype.css=function(){
    console.log('hello css')
},
jQuery.prototype.html=function(){ console.log('hello html')},...Copy the code

By adding methods to the prototype, jQuery instance objects can access these methods directly, but this seems too cumbersome to do if there are dozens or hundreds of methods, adding them every time is too redundant.

So, using the idea of stereotype substitution, change the stereotype to point to an object and add methods to that object. It saves a lot of code.

jQuery.fn = jQuery.prototype = {
   constructor: jQuery,  // Manually added the missing constructor attribute
   css: function() {
       console.log("css is ok again");
   }, 
   html: function() {
       console.log("html is ok again"); }}Copy the code

Among them, convenient to write, we will be the prototype of the jQuery jQuery. The prototype assigned to jQuery ‘constructors’ an attribute the jQuery. Fn, which can completely replace the former.

At this point, we can get a jQ object close to the original by creating a new jQuery object outside, and also access the methods and properties on the prototype chain. But what seems to be wrong is that the new object operation should be done internally? Yeah, we’re going to keep refining it.

Before we go any further with our little jQuery, let’s take a step back and remember how the factory function works.

About the factory function:

Creates an instance object and returns it.

functionPerson(name, age){ this.name = name; this.age = age; Var xm = new Person();"xm", 20) console.log(xm); Var xh = new Person("xh", 21); console.log(xh); // Create an xH objectCopy the code

Encapsulate the process:

 function $(name, age){//$is the factory function
    return new Person(name, age);
 }// Save the external new operation and get the instance object

var xm = $("xm".20);
console.log(xm);

var xh = $("xh".22);
console.log(xh);// Get two instance objectsCopy the code

As you can see, the packaged factory function can be called directly to create objects in batches. Let’s get back to jQuery

With that in mind, we’ll wrap the jQuery function as a factory function:

var jQuery = function (selector) {
    return new jQuery(selector);
}Copy the code

Are you done? Seems to be done? But there seems to be something wrong, how to look so familiar, this is not the recursion function next door, adjust yourself, play yourself to death. So… / so…. ? Can’t jQuery be used as a factory function? So the question is, who can do it if he doesn’t? Or is it not a constructor? It sounds a little confusing, but we were right!

In fact, in jQuery, the real constructors are not jQuery functions! We’re going to have to change our assumptions.

The real constructor of jQuery is jquery.fn.init. The real constructor is jquery.fn.init

What the hell is this jquery.fn.init? How did you push jQuery off the stage?

This jquery.fn. init is actually a method on the prototype of the jQuery function, which is the real constructor that creates objects.

So we’re going to have to change our code. Where to be, where to be:

var jQuery = function (selector) {
    return new jQuery.fn.init(selector);
}Copy the code

Init goes where it needs to go:

jQuery.fn = jQuery.prototype = {
    constructor: jQuery, // Add the constructor attribute manually
    init: function(selector) {
    var ele = document.querySelectorAll(selector); // This?? ==> init instance object
    Array.prototype.push.apply(this, ele); }}Copy the code

After this change, the reference of this has changed. The DOM objects stored on the jQuery prototype are now the children of init, and this refers to init. But our methods are all stored on the jQuery prototype, why do we need to move them back? Forget it. Too much trouble. Good thing there’s a prototype chain. Hand painted a sketch, will look at:



Prototype = jquery. prototype; prototype = jquery. prototype = jquery. prototype;



The code level is one sentence:

 jQuery.fn.init.prototype = jQuery.fn;Copy the code

At this point, our jQuery architecture is basically set up. At this point, outside of the sandbox, you don’t need to manually new, just call $(), such as $(‘div’), and you get the same object as jQuery.

All that is left is to add bricks and mortar and encapsulate some methods. Let’s take THE CSS method as an example to do a simple encapsulation.

The complete code is as follows:

(function(window, undefined) {

    var jQuery = function(selector) {  JQuery is a factory function
        return new jQuery.fn.init(selector); // Pass in a selector to instantiate the object
    }  Init is passed in the selector argument

    jQuery.fn = jQuery.prototype = {// The factory function prototype
        constructor: jQuery,
        init: function(selector) { // All the way from jQuery
            var ele = document.querySelectorAll(selector); // Get the object.
                 // this ==> is jquery.fn, which becomes an instance of init in new init ()
                 // Add the element to the instance object of init
            Array.prototype.push.apply(this, ele); / / to borrow. After new init(), this becomes the new object, and dom becomes the properties of the new object
        },

        css: function(name, value) {// What does the CSS method do by judging the number of arguments
            if (arguments.length === 2) {
                // Set a single style
               // Set all elements to this style
               // this ==>$("p"); The pseudo-array, which has the length property, is you need to style every item in the pseudo-array
               for (var i = 0; i < this.length; i++) {
                 this[i].style[name] = value; }}else if(arguments.length === 1) {/ / that is an object set multiple style | | for style
               if (typeof name === "object") { 
                   You need to set multiple styles for all the elements you fetch
                  for(var i = 0; i < this.length; i++){ 
                       //this[I] ==> each element
                      // The loop is the object, the set style and the style value
                      for(var k in name){  
                          this[i].style[k] = name[k]; }}}else if(typeof name === "string") {Note: Get the value of the first element
                    // this ==>$("p") this[0] ==> first element
                  // style operates on inline styles
                  / / window. GetComputedStyle (elements, null); Gets the style of its effect on the element
                  // Return value: an object
                    return window.getComputedStyle(this[0].null)[name]; }}return this;// Objective: to implement chain programming}}// Modify init's prototype object so that init instances can access methods on JQ
    jQuery.fn.init.prototype = jQuery.fn;
    window.jQuery = window.$ = jQuery; }) (window)Copy the code

CSS method encapsulation is a bit cumbersome, but CSS this stuff is not always like this, in JS CSS processing, laborious and uncomfortable. But there’s no good way. Fortunately, this part is easy.

However, there are two main features of jQuery that need to be noticed and must be done:

Implicit iteration and chained programming.

The former makes all styles set by JQ apply to all objects retrieved, while the latter requires that, at the end of the encapsulation of a method, the object must be returned for successive calls to implement chained programming. If the encapsulation method does not do either of these things, then the encapsulation has nothing to do with JQ, which should be taken care of.

[Conclusion] This article is a long time ago when learning to write fragments collated, limited to the personal level, the idea of JQ encapsulation may not understand in-depth, there may be many places to say not rigorous or paradoxical, but also please see the leaders don’t hesitate to point out. Thank you