11

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.

Hristo
  • 45,559
  • 65
  • 163
  • 230

6 Answers6

6

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);
Egor
  • 61
  • 2
  • @minibikini... what does the variable `app` represent? – Hristo Mar 29 '11 at 20:04
  • This is what @jamietre means by "setting up your own namespace". `app` is your namespaced global object that will contain your grouped functions and properties. `app` should be a very unique application name since it is your primary global object. – mVChr Mar 29 '11 at 20:05
  • 1
    Now wrap it in a closure! so you don't contaminate global scope with `app` – Raynos Apr 01 '11 at 15:28
4

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

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Matt Wonlaw
  • 12,146
  • 5
  • 42
  • 44
  • @mlaw... I'm actually starting to read his book "JavaScript - The Good Parts". I'll definitely check out the lessons. Thanks! – Hristo Mar 29 '11 at 20:00
  • The videos are much more informative than the book. Although I will put in the obligatory reference to another book - JavaScript: The Definitive Guide (http://www.amazon.com/JavaScript-Definitive-Guide-David-Flanagan/dp/0596101996/ref=sr_1_2?ie=UTF8&qid=1301429052&sr=8-2). –  Mar 29 '11 at 20:04
3

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.

Alex Heyd
  • 1,323
  • 1
  • 10
  • 17
  • @alex... this looks good, thanks! How is what you wrote different from the "namespace" idea other users are suggesting? – Hristo Mar 29 '11 at 20:10
  • @Hristo Well I'm actually just putting it into the jQuery namespace. Most people here are advising against it, so my code can just as easily be changed to be YOURNAMESPACE.ClassName = function(){} – Alex Heyd Mar 29 '11 at 20:19
  • also, @Hristo, the pattern I use here is up to debate. There are many alternatives, pick the one that's best suited for your case. I don't always use the above format, just most of the time. For example if I plan on having many many instances of the class, I would break out the methods into the class prototype for performance/memory reasons. – Alex Heyd Mar 29 '11 at 20:27
  • @alex... I appreciate your explanations. +1 as soon as I get my upvoting privileges back :) – Hristo Mar 29 '11 at 20:28
  • @alex... while you're around... what are you doing with this line `var cfg = $.extend(true, {}, this.defaults, options);`? – Hristo Mar 29 '11 at 21:02
  • @Hristo oh that extends the default options with the options passed in, so that way you can define a set of default options, override them with whatever is passed in as a parameter, and if a specific property isn't specified, it uses the default. http://api.jquery.com/jQuery.extend/ – Alex Heyd Mar 29 '11 at 21:36
1

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: How do I declare a namespace in JavaScript?

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

Community
  • 1
  • 1
Ender
  • 14,995
  • 8
  • 36
  • 51
  • @Ender... 1. I love your name :) 2. I am **not** developing a plugin. – Hristo Mar 29 '11 at 19:52
  • @Ender... thanks for the "Edit". That is the direction I want to head in. Now, I've seen this reference to "namespace" twice... what is that? How/Where would I learn more about creating my own namespace? – Hristo Mar 29 '11 at 20:01
  • I've just added a reference to another SO question which *hopefully* will answer that question :) – Ender Mar 29 '11 at 20:02
  • @Ender... awesome, thanks! As soon as I can get my upvoting ability back, I'll give you a +1. – Hristo Mar 29 '11 at 20:04
  • @Hristo - Thanks, glad I could help :) If you need further reading, try some google searches about javascript prototypes. There are TONS of resources explaining how to do it (and why). – Ender Mar 29 '11 at 20:06
  • @Ender... I'll definitely do a Google search in the near future. My goal here was to quickly get references to the BEST resources that the SO community would already know about :) – Hristo Mar 29 '11 at 20:07
0

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.

Jamie Treworgy
  • 23,934
  • 8
  • 76
  • 119
  • @jamietre... hmm... that makes a lot of sense! I am not trying to write a plug in so I see where my mistake is. The reason I spread things across files is so that I can group specific functionality together and locate it easily so that I don't have to scroll through one giant file. What do you mean by "create your own namespace"? Can you provide an explanation/sources/links/etc...? – Hristo Mar 29 '11 at 20:00
0

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.

  • @hal10001... can you provide more info on creating a namespace? This is actually the first time I've heard of this term :/ – Hristo Mar 29 '11 at 20:06