2

I am creating an AJAX API for a web service and I want to be able to call jQuery-like accessors. jQuery seems to be able to execute 'jQuery' as a function, but also use it to directly access the object that is the result of the function EG:

jQuery();
jQuery.each({});

This is the trick that I can't seem to pull off:

myAPI('foo'); //output: 'foo'
myAPI('foo').changeBar(); //output: 'foo' 1
myAPI.changeBar(); //Error: not a function

I have seen the answers to similar questions, which are helpful, but don't really answer my question.

#8734115 - Really interesting, but you can't access the methods that were set by f.prototype.

#2953314 - Uses Multiple operations to create object instead of a single function.

here is my code:

(function(window) {

        var h = function(foo) {
                // The h object is actually just the init constructor 'enhanced'
                return new h.fn.init(foo);
        };
        /**
         * Methods defined at protoype.
         */
        h.fn = h.prototype = {
            constructor: h,
            init: function(foo) {
                console.log(foo);
                return this;
            },
            splice : function () {},
            length : 0,
            bar : 0,
            changeBar : function() {
                this.bar++;
                return this.bar;
            }
        };
        h.fn.init.prototype = h.fn;

    //Publish 
    window.myAPI =h;

}( window));

I'm sure I'm missing something simple :(

Community
  • 1
  • 1
underscorePez
  • 897
  • 9
  • 19

2 Answers2

5

What jQuery is doing there is using jQuery as both a function and as a pseudo-namespace. That is, you can call jQuery: var divs = jQuery("div"); and you can use properties on it, e.g.: jQuery.each(...);.

This is possible because in JavaScript, functions are first-class objects, and so you can add arbitrary properties to them:

function foo() {
    alert("Foo!");
}
foo.bar = function() {
    alert("Bar!");
};

foo();     // "Foo!"
foo.bar(); // "Bar!"

That's literally all there is to it.

Within the call to bar, this will be the foo function (because this is determined entirely by how a function is called, not where it's defined). jQuery doesn't use this to refer to itself (usually it uses this to refer to DOM elements, sometimes to other things like array elements; when referring to itself, since it's a single thing, it just uses jQuery).

Now, you might want to ensure that your functions have proper names (whereas the function I assigned to bar above is anonymous — the property has a name, but the function does not). In that case, you might get into the module pattern:

var foo = (function() {
    function foo() {
        alert("Foo!");
    }

    function foo_bar() {
        alert("Bar!");
    }

    foo.bar = foo_bar;

    return foo;
})();

foo();     // "Foo!"
foo.bar(); // "Bar!"

That pattern also has the advantage that you can have private data and functions held within the scoping function (the big anonymous function that wraps everything else) that only your code can use.

var foo = (function() {
    function foo() {
        reallyPrivate("Foo!");
    }

    function foo_bar() {
        reallyPrivate("Bar!");
    }

    function reallyPrivate(msg) {
        alert(msg);
    }

    foo.bar = foo_bar;

    return foo;
})();

foo();               // "Foo!"
foo.bar();           // "Bar!"
reallyPrivate("Hi"); // Error, `reallyPrivate` is undefined outside of the scoping function

In your code, you're assigning things to the prototype property of the function. That only comes into play when the function is called as a constructor function (e.g., via new). When you do that, the object created by new receives the function's prototype property as its underlying prototype. But that's a completely different thing, unrelated to what jQuery does where it's both a function and a pseudo-namespace.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    Beat me to it. That's all there is to it! On your last point, Here's an article about using function constructors with or without `new`, http://js-bits.blogspot.com/2010/08/constructors-without-using-new.html, since the jQuery does work like a factory. – Ruan Mendes Feb 10 '12 at 16:43
  • So I can get the behaviour I want without having to employ jQuery's prototype magic. That makes things a lot easier. Thanks for sharing! – underscorePez Feb 13 '12 at 09:19
2

You do not need any of that weirdness, to use stuff like $.each you just attach functions to the function object instead of the prototype object:

function Constructor() {
    if (!(this instanceof Constructor)) {
        return new Constructor();
    }
}

Constructor.prototype = {

    each: function() {
        return "instance method";
    }

};

Constructor.each = function() {
    return "static method";
};


var a = Constructor();

a.each(); //"instance method"
Constructor.each(); //"static method"
Esailija
  • 138,174
  • 23
  • 272
  • 326