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
var Animal=function() {
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() {
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.
if(typeof Animals=='undefined') {
window["Animals"]={};
}
Animals.Base=function() {
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() {
this.numberOfLegs=4;
return this;
}
Animals.Mammal.prototype=new Animals.Base();
Animals.Dog=function() {
this.sound="Woef";
return this;
}
Animals.Dog.prototype=new Mammal();
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.
(function() {
if(typeof OurProduct=='undefined') {
window["OurProduct"]={};
}
var someVariableAvailableToAllClassesWithinThisFunctionButNotOutside="Hello";
if(typeof OurProduct.Animals=='undefined') {
window["OurProduct"]["Animals"]={};
}
OurProduct.Animals.Base=function() {
this.numberOfLegs=null;
this.sound=null;
return this;
}
.......
})();
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.
MyProduct.Objects.Person=function() {
}
We would like to have a function like NameSpace.register("MyProduct.Object")
. So, let's create that.
(function() {
Namespace = {
register : function(ns) {
var nsParts=ns.split(".");
var obj=window;
for(var i=0, j=nsParts.length; i<j; i++) {
if(typeof obj[nsParts[i]] == "undefined") {
obj=obj[nsParts[i]]={};
}
else {
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:
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.
Namespace("MyProject.Objects", function() {
MyProduct.Objects.Person=function() {
....
}
});
So how would we go about that? Let's create the namespace function:
(function() {
Namespace=function(ns, fs) {
var register = function(ns) {
var nsParts=ns.split(".");
var obj=window;
for(var i=0, j=nsParts.length; i<j; i++) {
if(typeof obj[nsParts[i]] == "undefined") {
obj=obj[nsParts[i]]={};
}
else {
obj=obj[nsParts[i]];
}
}
}
register(ns);
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.