How can jQuery behave like an object and a function?
jQuery
or $
seems to be a function:
typeof $; // "function"
And it acts like one:
$('div').removeClass(); // $ constructs a new object with some methods like removeClass
But when I drop the function parentheses it behaves like an object:
$.each(/* parameters */); // $ is an object with some methods like each
I'd like to know how this is possible and how I can implement this behaviour to my own functions.
Functions are also objects, so $.each
can be defined in a similar way as an Object.
JavaScript is a prototypical language. For jQuery, this means that every instance of $
inherits methods from jQuery.prototype
.See Notes
A very rough demo, to achieve the similar behaviour:
(function() { // Closure to not leak local variables to the global scope
function f(a, b) {
//Do something
}
// Prototype. All properties of f.prototype are inherited by instances of f.
// An instance of f can be obtained by: new f, new f(), Object.create(f)
f.prototype.removeClass = function(a) {
return a;
};
function $(a, b) {
return new f(a, b); // <--- "new f" !
}
$.each = function(a) {
alert(a);
};
window.$ = $; // Publish public methods
})();
//Tests (these do not represent jQuery methods):
$.each("Foo"); // Alerts "Foo" (alert defined at $.each)
alert($().removeClass('Blabla'));// Alerts "Blabla"
Notes
jQuery's root method is defined as follows (only relevants parts are shown):
(function(win) {
var jQuery = function (selector, context) {
return new jQuery.fn.init(selector, context, rootjQuery);
};
//$.fn = jQuery.fn is a shorthand for defining "jQuery plugins".
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function( /* ..parameters.. */ ) {
//.... sets default properties...
}
//....other methods, such as size, get, etc...
//.... other properties, such as selector, length, etc...
};
jQuery.fn.removeClass = function() { // (Actually via jQuery.fn.extend)
// ... method logic...
}; //...lots of other stuff...
win.$ = win.jQuery = jQuery; //Publish method
})(window);
The advantage of the prototype
method is that it's very easy to chain methods and properties. For example:
$("body").find("div:first").addClass("foo");
A method to implement this feature could be:
$.fn.find = function(selector) {
...
return $(...);
};
If you're interested in jQuery's real implementation, have a look at the annotated source code:
- jQuery core - Definitions of the constructor and base methods.
-
jQuery.fn.extend
is used to addremoveClass
, etc. to jQuery. - jQuery 1.7.1.
All functions work this way.
function fn() {
alert("hi mom");
}
fn.foo = "bar";
In JavaScript, functions are a type of object. Specifically, functions are instances of the Function object which is derived from Object. jQuery takes advantage of that fact and hangs some "static" methods from the jQuery function object.
jQuery's creator John Resig has a nice tutorial on this subject at http://ejohn.org/apps/learn/. That might give you some ideas about how to leverage this feature of JavaScript in your own code.