Skip to content Skip to sidebar Skip to footer

Creating A Jquery Like "$" Object

My end goal is being able to do something like this: MyVar(parameter).functionToPerform(); Silly enough, even after reading up on how variables are declared, looking at the jQuery

Solution 1:

As I write this, Squeegy's answer has the highest number of votes: 7. Yet it is wrong because __proto__ is non-standard and is not supported by Internet Explorer (even version 8). However, getting rid of __proto__ does not get it working either in IE 6.

This (somewhat simplified) is the way jQuery actually does it (even try it on IE 6), and it also includes examples of static methods and method chaining. For all the details of how jQuery does it, of course, you will have to check the jQuery source code yourself.

varMyClass = function(context) {
    // Call the constructorreturnnewMyClass.init(context);
};

// Static methodsMyClass.init = function(context) {
    // Save the contextthis.context = context;
};
MyClass.messageBox = function(str) {
    alert(str);
};


// Instance methodsMyClass.init.prototype.print = function() {
    return"Printing";
};
MyClass.init.prototype.move = function() {
    returnthis.context;
};

// Method chaining exampleMyClass.init.prototype.flash = function() {
    document.body.style.backgroundColor = '#ffc';
    setInterval(function() {
        document.body.style.backgroundColor = '';
    }, 5000);
    returnthis;
};


$('#output').append('<li>print(): '+ MyClass().print() +'</li>');
$('#output').append('<li>flash().move():'+ MyClass('azerty').flash().move() +'</li>');
$('#output').append('<li>context: '+ MyClass('azerty').context +'</li>');
MyClass.messageBox('Hello, world!');

Note that if you need "private" data, you will have to put instance methods inside MyClass.init (with a variable declared just inside that function) as this.print = function() { ... }; instead of using MyClass.init.prototype.

Solution 2:

jQuery() is both a module with global methods, and a constructor. It automatically calls a constructor if it needs to. If we are not called with a new keyword, then this will not have been constructed with MyClass. We can detect that and call the function in constructor mode instead. Once we do that, then this will be an instance of MyClass and we can start adding stuff to it.

varMyClass = function(context) {
    // if the function is called without being called as a constructor,// then call as a constructor for us.if (this.__proto__.constructor !== MyClass) {
        returnnewMyClass(context);
    }

    // Save the contextthis.context = context;

    // methods...this.print = function() {
        return"Printing";
    }

    this.move = function() {
        returnthis.context;
    }
};

$('#output').append('<li>print(): '+ MyClass().print() +'</li>');
$('#output').append('<li>move():'+ MyClass('azerty').move() +'</li>');
$('#output').append('<li>context: '+ MyClass('azerty').context +'</li>');

http://jsfiddle.net/rvvBr/1/

Solution 3:

when you do

var test = new MyClass()

you create an object that has two attributes move and print. You object test is no more a function because of the new statement. So calling test() is wrong.

Solution 4:

every time you call $ in jQuery it returns a new jQuery.init object. The jQuery.init object has functions that are then called.

functiontest(type)
{
  switch (type)
  {
    case'azerty':
      returnnewtype.a();
    case'qwerty':
    default:
      returnnewtype.b();
  }
}

test.a = function()
{
  //a object defined
};

test.a.prototype.move = function()
{
  //move function defined for the a object
};

etc...

I just typed this on the fly, so it may need some tweaking

This would allow you to call test('azerty').move();. More importantly: I hope you can see the general structure being used.

Edit to add:

To continue chaining functions like in jQuery, make sure you return the this object at the end of each function call:

test.a.prototype.move = function()
{
  //move function defined for the a objectreturnthis;
};

Solution 5:

[Probably] The Most Elegant Solution

First off, jQuery uses a pattern which is closer to a Monad, a Factory, or a combination of both. Nevertheless, here's what I've been using in my projects because the pattern, itself, is so loosely coupled to whatever class you'd like to utilize:

;(function (undefined) {
    if (undefined) return;
    varENV = this;

    varClass = functionClass() {
        var thus = this;

        functionfind(data) {
            console.log('@find #data', data);
            returnthis;
        }

        functionshow(data) {
            console.log('@show #data', data);
            returnthis;
        }

        // export preceptsthis.find = find;
        this.show = show;

        returnthis;
    };

    varNamespace = ENV['N'] = new (functionNamespace(Class) {
        var thus = this;

        varNs = Class.apply(functionNs(data) {

            if (thisinstanceof N) {
              returnnewNamespace(Class);
            }

            returnNs.find.apply(Ns, arguments);
        });


        returnNs;
    })(Class);

}).call(window || newfunctionScope() {});

var n = N('#id').show(450);
var m = newN();

m('#id')('.curried').show('slow');

console.log(n !== m);  // >> true

Basically, you can use it as a function, an object, and use the new keyword to construct another unique object/function. You can use this to enforce an arbiter method (default, like the find method above), or use different methods based upon what parameters are input. For instance, you can do something like:

var elementsList = N('#id1')('#id2')('#otherSpecialElement').each(fn);

-- OR --

var general = N('.things');
var specific = general('.specific')('[data-more-specific]').show();

The above would, for instance, accumulate a nodelist of multiple elements (1st expression), or drill down to one specific element (2nd).

Hope this helps

Post a Comment for "Creating A Jquery Like "$" Object"