Thursday, May 3, 2012

Java-esque OOP in JavaScript and a jQuery fail


I'm working on a project and I'm really trying to write object-oriented JavaScript code. I have just started reading Douglas Crockford's JavaScript: The Good Parts and I'm quickly beginning to realize that writing Java-esque OOP in JavaScript will be a difficult task.



Thus far, I've written something like the following...




// index.html
$(document).ready(function() {

$().SetUpElements();
});

// this is in a different js file
$.fn.SetUpElements = function() {

// do stuff here
$().UpdateElement();
};

// this is in yet another different js file
$.fn.UpdateElement = function() {

// do stuff here
var element = new Element(id, name); // continue doing work
};

function Element(id, name) {

var id = id;
var name = name;

// other stuff
};



... the idea being that I want objects/functions to be refactored and decoupled as much as possible; I want to reuse as much code as I can. I've spread a lot of my code across different .js files with the intention of grouping specific relevant code together, much like if you would write different classes in Java.



As I've been learning more about jQuery, I realized that the notation $.fn.foo = function() { ... }; is actually adding this foo function to the prototype of all jQuery objects. Is this something I should be doing? Am I misusing jQuery somehow?



I would appreciate suggestions on how to improve my approach to OOP in JavaScript and I would love to see references to sources/tutorials/articles/etc... that discuss this topic. Please feel free to provide feedback even if an answer has been selected. I am looking for your advice... this is why I posted :)



** Note: I'm not developing a jQuery plugin. I'm developing a web app and heavily making use of jQuery.


Source: Tips4all

6 comments:

  1. I would say the first way you're creating methods is a misuse of jQuery. The jQuery.fn.foo syntax is generally reserved for functions that act upon a DOM element but you're using them as static functions, by using an empty jQuery object.

    If you want to create static functions under the jQuery namespace, you can do:

    jQuery.foo = function(){};


    then call it via:

    jQuery.foo();


    instead of:

    jQuery.fn.foo = function(){};


    which allows you to do:

    jQuery('#someElementId').foo();


    In terms of OOP. there are many different approaches (module pattern, prototype, factory...). The way I generally approach it, is create a Class as a static function, then invoking it with the keyword new

    (function($){
    var undefined;

    $.ClassName = function(options){
    var self = this;
    var cfg = $.extend(true, {}, this.defaults, options);

    // ********************
    // start:private
    // ********************
    function _init(){

    };

    // ********************
    // start:public
    // ********************
    this.methodName = function(){

    };

    _init();
    };

    $.ClassName.prototype.defaults = {};
    })(jQuery);


    In terms of reusing functionality, there's a threshold after which decoupling is more detrimental than anything. Make sure you keep the right balance of modularity and organization.

    ReplyDelete
  2. If it's not a jquery plugin, I would write something like this:

    var app = {
    init: function(){
    app.SetUpElements();
    },
    SetUpElements: function() {
    return 'something';
    },
    etc: function() {

    }
    };
    $(document).ready(app.init);

    ReplyDelete
  3. In reference to doing OO in Javascript you should watch Douglas Crockford's Advanced Javascript lessons

    ReplyDelete
  4. If you are developing a plug-in, it is generally best to avoid contaminating both the jQuery and Global namespaces/prototypes as much as possible.

    Have a look at this documentation on jQuery's website, which gives an excellent explanation of how to provide access to a variety of reusable functionality, while only claiming a single name on the jQuery object.

    Another method I have seen used is to provide a single init function for your plug-in on the jQuery namespace, and then make an additional plug-in specific name available in the Global namespace, which provides more direct access to function/members, etc. The Galleria plug-in is an excellent example of how to do this well. Examine their documentation, and the way they set up the structure of the plug-in.

    EDIT

    After seeing that you are not making a plug-in: Many of the things that I said still apply to general jQuery development (and really, to development in general).

    In your specific case, since you are not making use of the jQuery selectors in making your function calls, these functions could probably be just as easily attached to a prototype of your own making, which could be placed in the Global namespace.

    As far as namespacing goes, have a look at the accepted answer on this question for an example of how to do it properly: Javascript Namespace Declaration

    And here is a great resource for OO in JavaScript. I used this article when trying to wrap my head around the concept: http://mckoss.com/jscript/object.htm

    ReplyDelete
  5. It looks like you're writing all your code as jQuery plug-ins? I wouldn't do that. Create your own namespace.

    What's the goal in putting everything in different files? Unless they are scripts that will be used for specific pages, that just makes it harder to manage things. These all look like general library functions. Unless you think you'll want to load some on one page, but not all, then put them all in one file.

    Object oriented programming doesn't mean fragmenting stuff across files. That's just organization, you should break things into different files when you don't need all of the contents on some page.

    ReplyDelete
  6. As most have already mentioned, unless you are explicitly creating a public plugin, do not use jQuery custom functions. Instead create your own namespace using straightforward object notation, or something more robust like the Module Pattern. If you want to get crazy you can use an API on top of an API to get a more classical OO pattern by using Dean Edward's Base. The end goal is to extract your functional and design requirements from the jQuery API. That way if you ever needed to port something over to Mootools or Prototype it would be much simpler to accomplish.

    ReplyDelete