30

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.

js-coder
  • 8,134
  • 9
  • 42
  • 59
  • 7
    Functions are objects. The main difference is that you can invoke functions – Raynos Jan 04 '12 at 21:16
  • 1
    possible duplicate of [How does jQuery makes the jQuery object both a function and an object property?](http://stackoverflow.com/questions/7073647/how-does-jquery-makes-the-jquery-object-both-a-function-and-an-object-property) – starwed Jul 21 '13 at 00:44

3 Answers3

33

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:

Rob W
  • 341,306
  • 83
  • 791
  • 678
  • This is only alerting 4 for me, not 3! – js-coder Jan 04 '12 at 21:20
  • 1
    @dotweb Try it with the 2nd line as `alert(new $().removeClass());`. jQuery enables the `new` keyword to be optional, but you'll need it for this demo. – Jonathan Lonowski Jan 04 '12 at 21:27
  • 1
    @JonathanLonowski Thanks, this is neat! I see, the `new` keyword is needed to construct the prototype. How can I make it optional like jQuery does? – js-coder Jan 04 '12 at 21:29
  • 2
    @dotweb The trick for it is to test if `this instanceof Constructor` within `Constructor`. And, if it isn't, `return new Constructor(arg1, arg2);`. You can find John Resig's summary of this [on his blog](http://ejohn.org/blog/simple-class-instantiation/). You can also find a number of performance-related variants on [JS Perf](http://jsperf.com/optional-new-keyword). – Jonathan Lonowski Jan 04 '12 at 21:46
  • 2
    @dotweb Apologies for my incomplete answer, I had to go very quickly. I have just finished my answer. – Rob W Jan 04 '12 at 22:14
11

All functions work this way.

function fn() {
    alert("hi mom");
}

fn.foo = "bar";
recursive
  • 83,943
  • 34
  • 151
  • 241
3

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.

dgvid
  • 26,293
  • 5
  • 40
  • 57