Understanding how jQuery works internally

Viewed 1005

So I am trying to understand how jQuery works internally. jQuery exposes a global $ variable, which can be used like:

$('.abc') // returns a list of DOM nodes having class abc
$.ajax() // used to perform AJAX requests

As you can see, $ is a function, from statement 1, while it's an object, from statement 2. Now I know that this is possible because of function Objects in JavaScript, meaning that a function is also an Object in JavaScript, and hence you can attach methods to it. But I am not sure how this is achieved internally?

I was looking at this blog post which explains how the 'constructor pattern' can be used to mimic jQuery, but how would you attach methods on $ using that pattern? Or does jQuery use some completely different pattern? Here is a small implementation of jQuery from that. This code can do $(selector), but how do I add $.method in it?

// Mini jQuery implementation 
var $ = (function() {
  var JQuery = function(selector) {
    if (selector === "document") {
      this.elements = [document];
    } else if (selector === "window") {
      this.elements = [window];
    } else {
      this.elements = document.querySelectorAll(selector);
    }
    return this;
  };

  JQuery.prototype.each = function(callback) {
    this.elements.forEach(function(element) {
      callback(element);
    });
    return this;
  };

  JQuery.prototype.addClass = function() {
    if (arguments.length === 0) return this;
    else {
      this.each(element => {
        element.classList.add(...arguments);
      });
      return this;
    }
  };

  return function(selector) {
    return new JQuery(selector);
  };
})();

// Use our mini jQuery to turn background of matching Nodes green
$(".abc").addClass("highlight");
.abc {
  background : red;
}

.abc.highlight {
  background: green;
}
  <div class="abc">Node 1</div>
  <div class="abc">Node 2</div>

1 Answers

The constructor pattern actually does not solve the problem of $.method, if you want to achieve the $.method you have to work around a little bit, this is a simple solution based on your example :

var $ = (function() {
  
  var JQuery = function(selector) {
    if (selector === "document") {
      this.elements = [document];
    } else if (selector === "window") {
      this.elements = [window];
    } else {
      this.elements = document.querySelectorAll(selector);
    }
    return this;
  };

  JQuery.prototype.each = function(callback) {
    this.elements.forEach(function(element) {
      callback(element);
    });
    return this;
  };

  JQuery.prototype.addClass = function() {
    if (arguments.length === 0) return this;
    else {
      this.each(element => {
        element.classList.add(...arguments);
      });
      return this;
    }
  };

  return function(selector) {
    
    // A simple function for extending object properties/values to another object
    $.extend = JQuery.prototype.extend = function(source) {
      if (typeof arguments[0] !== 'object') return;

      for (var prop in source) {
        if (source.hasOwnProperty(prop)) {
          this[prop] = source[prop];
        }
      }
    };
    
    // Extend all the JQuery prototype methods to the '$' object
    $.extend(JQuery.prototype);
    
    // Others extensions that's can only accessed via '$.method' and not '$('selector').method'
    $.extend({
      ajax: function() {},
      noop: function() {},
    });
    
    return new JQuery(selector);
  };
})();

// Use our mini jQuery to turn background of matching Nodes green
$("body").addClass("highlight");
$.addClass.call($('body'), 'selected');

jQuery doesn't use the constructor pattern for the $ or jQuery objects, if you check this you can see that's the $ or jQuery is just a constructor function of the init function jQuery.fn.init defined here, all the initialized jQuery object $("body") methods are attached to the jQuery.fn (alias for jQuery.prototype) (this is one example here), the other methods like ajax, noop, isReady are attached directly to the $ object (example of ajax method here). that's why we can access those methods with $.method syntax.

Sorry for my bad English, I hope this makes sense to you.

Edit1: For anyone who's interested in how jQuery works internally and want to know more about jQuery core, I've created this github repo how-jQuery-work especially for this purpose. Thanks!

Related