48

It seems there are so many ways to set up a JavaScript application so it is confusing as to which one is correct or best. Are there any difference to the below techniques or a better way of doing this?

MyNamespace.MyClass = {
    someProperty: 5,
    anotherProperty: false,

    init: function () {
        //do initialization
    },

    someFunction: function () {
        //do something
    }
};

$(function () {
    MyNamespace.MyClass.init();
});

Another way:

MyNamespace.MyClass = (function () {
    var someProperty = 5;
    var anotherProperty = false;

    var init = function () {
        //do something
    };

    var someFunction = function () {
        //do something
    };

    return {
        someProperty: someProperty
        anotherProperty: anotherProperty
        init: init
        someFunction: someFunction
    };
}());

MyNamespace.MyClass.init();

The first technique feels more like a class. I am coming from server-side background if this makes a difference. The second technique seems more redundant and a bit awkward, but I see this used a lot too. Can someone please help shed some light and advise the best way to move forward? I want to create a application with lots of classes talking to each other.

Naftali
  • 144,921
  • 39
  • 244
  • 303
TruMan1
  • 33,665
  • 59
  • 184
  • 335
  • 1
    This isn't using OOP JS so much as using the module pattern. – zzzzBov Jul 25 '12 at 14:04
  • Also, you've implied that the two methods are equivalent, but the second method is not valid. The way you've declared the private variables is syntactically invalid, you just want a regular var statement. – alexp Jul 25 '12 at 14:15
  • Thx I fixed the second method. – TruMan1 Jul 25 '12 at 14:18
  • If you're interested in learning more, I would highly recommend Douglas Crockford's [Javascript: The Good Parts](http://shop.oreilly.com/product/9780596517748.do). He not only offers a Javascript approach to classical OOP, but also explains how Javascript's more LISP-like parts can be used to create some other amazing structures. Starting from the ground-up, he explains how and why each structure works and what works best. Also, you may personally be interested in learning [CoffeeScript](http://coffeescript.org/), especially for its built-in [class structure](http://coffeescript.org/#classes). – thirdender Jul 25 '12 at 15:01

5 Answers5

65

Do neither of those things.

Make a javascript "class":

var MyClass = function () {

    var privateVar; //private
    var privateFn = function(){}; //private 

    this.someProperty = 5;  //public
    this.anotherProperty = false;  //public
    this.someFunction = function () {  //public
        //do something
    };

};

MyNamespace.MyClass = new MyClass();

One with static vars:

var MyClass = (function(){

    var static_var; //static private var

    var MyClass = function () {

        var privateVar; //private
        var privateFn = function(){}; //private 

        this.someProperty = 5;  //public
        this.anotherProperty = false;  //public
        this.someFunction = function () {  //public
            //do something
        };
    };

    return MyClass;

})();

MyNamespace.MyClass = new MyClass();

With a "constructor" (all of the examples have a "constructor", this one just has parameters to work with):

var MyClass = function (a, b c) {

    //DO SOMETHING WITH a, b, c <--

    var privateVar; //private
    var privateFn = function(){}; //private 

    this.someProperty = 5;  //public
    this.anotherProperty = false;  //public
    this.someFunction = function () {  //public
        //do something
    };

};

MyNamespace.MyClass = new MyClass(1, 3, 4);

With all of the above you can do:

MyNamespace.MyClass.someFunction();

But you cannot do (from the outside):

MyNamespace.MyClass.privateFn(); //ERROR!
Naftali
  • 144,921
  • 39
  • 244
  • 303
  • How do I handle the init or a constructor? – TruMan1 Jul 25 '12 at 14:08
  • @TruMan1 the "constructor" is whatever you pass into the `new MyClass()`. I will add an example with a constructor. – Naftali Jul 25 '12 at 14:09
  • A "constructor" doesn't have to have parameters. – nnnnnn Jul 25 '12 at 14:24
  • @nnnnnn that is true. I just put it there for flair. – Naftali Jul 25 '12 at 14:26
  • @nnnnnn I added that note to the post. – Naftali Jul 25 '12 at 14:26
  • 1
    How would I avoid conflicts with another loaded library who creates a "var MyClass" in the global namespace as well? Is there no way around this and have to choose very unique names for my classes like "var TrumanMyClass = function ()..."? – TruMan1 Jul 25 '12 at 16:57
  • Or just name ur class within your namespace ^_^ there is no reason to make it global, I just put it there as an example – Naftali Jul 25 '12 at 17:06
  • How can you define the class directly into the namespace then? I've been struggling with this for a couple of days now - there is clearly something with the design of JavaScript I'm not grasping... :( – thomthom Jun 11 '13 at 18:37
  • Learnt a lot from this answer. Thanks mate. –  Dec 04 '13 at 08:51
  • Doesn't this answer need an update since ES6? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes – AndrewRMillar Jun 01 '20 at 17:34
  • Not that I know of @Sl4rtib4rtf4st -- class is basically syntactic sugar around what exists here. – Naftali Jun 03 '20 at 00:25
  • That completely defeats the purpose of a namespace, since it is still defined in the global scope! – beliha Aug 28 '23 at 06:13
6

The first example is simply an Object literal - it cannot be instantiated and doesn't have private members. The second example has some incorrect syntax (var someProperty: 5 should be var someProperty = 5) but is using a closure to encapsulate internal private state within a self-invoking anonymous function.

The second approach looks better for encapsulating private members, but could be made more "Object-oriented" by making it an instantiable class:

MyNamespace.MyClass = function() { ... };
MyNamespace.MyClass.prototype.someProperty = 'foo';

Then you can instantiate it with the 'new' keyword:

var aClass = new MyNamespace.MyClass();
aClass.init(...);
chrisfrancis27
  • 4,516
  • 1
  • 24
  • 32
4

I use the following syntax for the instantiable classes with namespace

 var MYNamespace = MYNamespace|| {};

 MYNamespace.MyFirstClass = function (val) {
        this.value = val;
        this.getValue = function(){
                          return this.value;
                       };
    }

var myFirstInstance = new MYNamespace.MyFirstClass(46);
alert(myFirstInstance.getValue());

jsfiddle: http://jsfiddle.net/rpaul/4dngxwb3/1/

Razan Paul
  • 13,618
  • 3
  • 69
  • 61
3

Why you should never use

 return { methodName : methodDelegate}

like in second example:

MyNamespace.MyClass = (function () {
    var someProperty = 5;

    var init = function () {
        //do something
    };

    return {
        someProperty: someProperty
        someFunction: someFunction
    };
}());

MyNamespace.MyClass.init();

When you use namespace you have to think about it as about declaration, not the instance.

MyNamespace = {};
MyNamespace.sub = {};
MyNamespace.anotherSub = {};
MyNamespace.sub.MyClass = (function () {

    var static_var; //static private var

    var MyClass2 = function () {

        var privateVar; //private
        var privateFn = function () { }; //private 

        this.someProperty = 5;  //public
        this.anotherProperty = false;  //public
        this.someFunction = function () {  //public
            //do something
        };
    };

    return MyClass2;

})();
debugger;

var c1 = new MyNamespace.sub.MyClass();
c1.someProperty = 1; // creates 5->1.

var c2 = new MyNamespace.sub.MyClass();
c2.someProperty = 2;  // creates 5->2. c1 is still 1



debugger;
var myClass = function () {
    var someProperty = 5;
    var anotherProperty = false;

    var init = function () {
        //do something
    };

    var someFunction = function () {
        //do something
    };

    return {
        someProperty: someProperty,
        anotherProperty: anotherProperty,
        init: init,
        someFunction: someFunction
    };
};


MyNamespace.MyClass = myClass();
var c2 = MyNamespace.MyClass;
// how  are planning to create one more object, while it's a reference? copy      //the whole one?

c2.someProperty = 2; // changes 5 -> 2
var c3 = MyNamespace.MyClass.init(); // create 2 instead of 5

c3.someProperty = 3;    // changes c3 and c3 from 2 to 3.
console.log(c2.someProperty + c3.someProperty);

And no metter how much Module anti-patter was popular. Declaration gives you an ability to use the same code with different instances in an expected way for other developers . The quality of code and simplicity of its reading increases. The goal of any developer is to write a simple code to be read, not a shorter or D.R.Y. - but simple to be read and be understanded by another developer. That decreases the number of bugs first. (c) S. McConnell

Artem A
  • 2,154
  • 2
  • 23
  • 30
2

How to combine namespace and class declaration:

var ns = { // your namespace
    my_value: 1, // a value inside the namespace to avoid polluting
    MyClass: function() { // a class inside the namespace
        this.class_property: 12,
        this.class_method: function() {
            console.log("My property: " + this.class_property);
        }
    },
    myFunction: function() { // a function inside a namespace
        console.log("I can access namepsace value if you don't use 'new': " + this.my_value);
    }
};

Accessing your value:

console.log(ns.my_value);

Now, for the Class and Function: If you use new, the word this inside the function will point to it's constructor. If you don't use new, this will point to the namespace. So,

Using a Class:

var obj = new ns.MyClass();

Using the Function:

ns.myFunction();

If you construct the object without new, this will point to the namespace, so the namespace will be "destroyed" because MyClass.class_property and MyClass.class_method will be added to it.

marirena
  • 300
  • 2
  • 10