angus angus - 4 months ago 6
jQuery Question

How does jQuery have the $ object constructor and the methods associated with the $?

How is it that jQuery can do

$("#foo").addClass("bar")
and
$.ajax()
?

I'm creating a micro javascript framework and want to create a new instance of an object, such as
$("#hello")
. With this object there are associated methods, such as
addClass
,
css
, etc, just like with jQuery. So I could do something like

$("#foo").addClass("remove").css("color", "red");


I have been successful in creating this. However, when I want to call a method from this object, such as
$.ajax
, the constructor function is overwritten, and I can call $.ajax, but not $("#foo").

Basically, how can jQuery do both?

Answer

OK, the $ function is not only a function but an object, like all functions. So it can have methods. That's all that ajax is, a method of the $ function. So we can start off by doing this:

$ = function(obj) {
  // some code 
};
$.ajax = function (arg1, arg2) {
  // some ajax-y code
};

So far so good. Now, what on earth do we put in the $ function? Well it has to return an object and that object has to have some nice methods defined on it. So we'll need a constructor function (to give us new objects) and a prototype (to provide the nifty methods for those objects).

$ = function(obj) {
  var myConstructor = function (obj) {
    this.wrappedObj = obj;
  };

  myConstructor.prototype = {
    niftyMethod: function () {
      // do something with this.wrappedObj
      return this; // so we can chain method calls
    },
    anotherNiftyMethod: function (options) {
      // do something with this.wrappedObj and options
      return this; 
    }
  };

  return new myConstructor(obj);
};

So there we have it. We can do this:

var mySnazzObject = $("whatever");
mySnazzObject.niftyMethod().anotherNiftyMethod(true);        

And we can do this:

$.ajax("overthere.html", data);

Obviously jQuery does a heck of a lot more than that, and it does it in some really impressive ways, but that's the general idea.

UPDATE: AS @Raynos was kind enough to observe without supplying a constructive answer, my original code would create the prototype ad infinitum. So we make use of an anonymous autoexecuting function to declare the constructor and prototype separately:

(function () {
  var myConstructor = function (obj) {
    this.wrappedObj = obj;
  };

  myConstructor.prototype = {
    niftyMethod: function () {
      // do something with this.wrappedObj
      return this; // so we can chain method calls
    },
    anotherNiftyMethod: function (options) {
      // do something with this.wrappedObj and options
      return this; 
    }
  };

  var $ = function(obj) {
    return new myConstructor(obj);        
  };

  $.ajax = function (arg1, arg2) {
    // some ajax-y code
  };

  window.$ = $;  
}());