Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Javascript

JavaScript namespacing

4.33/5 (9 votes)
28 Mar 2013CPOL3 min read 30.6K  
Creating namespaces through a function.

Introduction

Anyone that comes from a C#, Java background knows the importance of namespaces. It's the primary way to keep your code organized. JavaScript doesn't support namespaces, or does it?

Object nesting 

JavaScript is a dynamic language. You can create objects on the fly, and objects can contain other objects. This concept will allow for creating a structure of prototypes that mimic namespacing.

Let's look at the mammal, cat and dog example

JavaScript
var Animal=function()   {
    //we don't know yet
    this.numberOfLegs=null;
    this.sound=null;
    return this;
}
Animal.prototype.getNumberOfLegs=function() {
    return this.numberOfLegs;
}

Animal.prototype.makeSound=function() {
   alert(this.sound);
}

var Mammal=function()   {
    //all mammals have for legs;
    this.numberOfLegs=4;
    return this;
    }
Mammal.prototype=new Animal();

var Dog=function()  {
    this.sound="Woef";
}
Dog.prototype=new Mammal();

var Cat=function()  {
    this.sound="Miauw";
    return this;
}
Cat.prototype=new Mammal();

var Tiger=function()    {
    this.sound="Wroaar";
    return this;
}
Tiger.prototype=new Mammal();

var Leopard=function()    {
    this.sound="Grrrr";
    return this;
}
Leopard.prototype=new Mammal();

This is great. We could create all the mammals and we could make a base class for the birds etc... But what if we also needed to keep a list of IOS names. To list apples IOS's we'd need Tiger and Leopard.

And next to that let's consider Animal to be a class you shouldn't use directly. I tend call such classes Base. But with no namespace I can have just one base.

All in all we'd like to create our animals in the Animals namespace. Allowing for an IOS namespace that also has Tiger & Leopard and a Base class. The way we trick JS into mimicking namespaces is to create nested objects. In which we place other objects.

JavaScript
//Create the Animals namespace

if(typeof Animals=='undefined')  {
    window["Animals"]={};   
}

Animals.Base=function()   {
    //we don't know yet
    this.numberOfLegs=null;
    this.sound=null;
    return this;
}
Animals.Base.prototype.getNumberOfLegs=function() {
    return this.numberOfLegs;
}

Animals.Base.prototype.makeSound=function() {
   alert(this.sound);
}

Animals.Mammal=function()   {
    //all mammals have for legs;
    this.numberOfLegs=4;
    return this;
    }
Animals.Mammal.prototype=new Animals.Base();

Animals.Dog=function()  {
    this.sound="Woef";
    return this;
}
Animals.Dog.prototype=new Mammal();

//...etc

Notice the omission of 'var'. What we are doing here is add everything to the global namespace. To some this is considered a no-no, but it is exactly what jQuery, ExtJs, YUI and prototype do. The truth of the matter is, if you place all your classes and even functions in a single namespace, say your products name, then you'll actually prevent polluting the global namespace.

On top of that I tend to wrap all the code in a .js file inside an anonymous function. That way, you can have 'global' variables that are only available inside that source, and they won't pollute the global namespace.

JavaScript
(function() {
    //Create OurProduct namespace
    
    if(typeof OurProduct=='undefined')  {
        window["OurProduct"]={};   
    }
    
    var someVariableAvailableToAllClassesWithinThisFunctionButNotOutside="Hello";
    
    //Create the Animals namespace inside the OurProduct namespace
    
    if(typeof OurProduct.Animals=='undefined')  {
        window["OurProduct"]["Animals"]={};   
    }
    
    OurProduct.Animals.Base=function()   {
        //we don't know yet
        this.numberOfLegs=null;
        this.sound=null;
        return this;
    }
    .......
})();
//Note the () at the end. If you omit that this code will never be run.

Register Namespace

Namespacing this way is actually nothing more than creating nested objects. And since window itself is an object you can actually create objects directly into it. But if we would like to create an object in a non-existent  nested namespace, we'll need to create that first.

JavaScript
MyProduct.Objects.Person=function() {
    //This will fail, because MyProduct.Objects doesn't yet exist.
}

We would like to have a function like NameSpace.register("MyProduct.Object"). So, let's create that.

JavaScript
(function() {
    Namespace = {
        register : function(ns) {
            //Split the string
            var nsParts=ns.split(".");
            //Store window in obj
            var obj=window;
            //Travese through the string parts
            for(var i=0, j=nsParts.length; i<j; i++)    {
                
                if(typeof obj[nsParts[i]] == "undefined") {
                    //Is it did not exist create it and copy it to obj
                    //so the next part can be copied into this part.
                    obj=obj[nsParts[i]]={};
                }
                else    {
                    //Still copy it to obj, cause the next one might not exist
                    obj=obj[nsParts[i]];
                }
            }
        }
    }

    NameSpace.register("MyProduct.Objects");
    MyProduct.Objects.Person=function() {
        alert("yeee this is legal")
    }   
    var P=new MyProduct.Objects.Person();
})();

This is getting somewhere. I've had this exact function in my projects for years. Until I started thinking, "since I always wrap my classes in an anonymous function, why not make that function useful?" Next to that my C# code looks like this:

C#
namespace MyProduct.Objects {
    
    public class Person {
        ....
    }
}

Of course we can't and shouldn't try make the code look the exact same way, but we could have something very similar.

JavaScript
Namespace("MyProject.Objects", function()  {
    
    MyProduct.Objects.Person=function() {
        ....    
    }
});
//notice the omission of the ()
//starting the function will be a task of the namespace function

So how would we go about that? Let's create the namespace function:

JavaScript
//This function itself is'nt Namespaced so it should be wrapped in an anonymous function
(function() {
    //no var so it will end up in the global namespace
    Namespace=function(ns, fs)   {
        //ns Namespace as . seperated string
        //fs the return function
        
        //Here's our trusty register function, only now it's inline 
        var register = function(ns) {
            //Split the string
            var nsParts=ns.split(".");
            //Store window in obj
            var obj=window;
            //Travese through the string parts
            for(var i=0, j=nsParts.length; i<j; i++)    {
                
                if(typeof obj[nsParts[i]] == "undefined") {
                    //Is it did not exist create it and copy it to obj
                    //so the next part can be copied into this part.
                    obj=obj[nsParts[i]]={};
                }
                else    {
                    //Still copy it to obj, 'cause the next one might not exist
                    obj=obj[nsParts[i]];
                }
            }
        }      
        //Let's register the namespace
        register(ns);
        
        //And call the wrapper function. 
        fs();     
    };
})();

I'm using uppercase for the first letter of the Namespace. Normally I would start a function with lowercase. But for instance the word class is a reserved word. So I fear namespace might become a reserved word in the future.

Conclusion

By many JavaScript is not considered a 'real' language. But whatever your opinion is, that's no reason to just start hacking away with long function chains and if then else's in the page code. As soon as you run in to anything that could be considered to have a state, that could change over time, you'll need to start thinking about creating objects for it. And as soon as you go there, you'll need a namespace to put them in. It might seem ridiculous at first, but the bigger you're codebase gets the more sense it will make.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)