Monday, December 20, 2010

JavaScript Namespaces

In my JavaScript programming I have been putting all of my classes and variables in the global namespace. Since global variables are considered evil, I want to clean up my code. The obvious thing to do is to use namespaces, but JavaScript doesn't support namespaces. Luckily JavaScript is powerful enough that you can fake it pretty well. StackOverflow has a discussion on ways to do it. A couple ideas that I really like are the Module Pattern and a namespace library.

The main idea is that you create an object to act as a namespace and then you make all of your objects and functions and classes (which are really function objects) be properties of this "namespace" object. Here is the approach I've decided to take with the JavaScript project I've been working on. This may not be the best way to do it, but it is what I am trying for now as I learn this.

Global Namespace Object
First, I need to create a global namespace object which should have a unique name. I'll call it myNamespace, and here is how it is defined:
  1 var myNamespace = {   
  2   namespace : function(name, func) { 
  3     var parts = name.split("."); 
  4     var space = this;
  5     for (var i in parts) {
  6       var part = parts[i]; 
  7       var next = space[part]; 
  8       if (!next) next = {}; 
  9       next.namespace = this.namespace; 
 10       space[part] = next; 
 11       space = next; 
 12     }     
 13     if (func) { 
 15     } 
 16     return space; 
 17   } 
 18 };
Basically, it is just an object. What it provides is a namespace method which takes a dot.separated.namespace and returns the namespace object corresponding to that particular name, creating it if necessary. This means that if I make the call myNamespace.namespace("dot.separated.namespace"), it will create the objects:,, and I've made the decision to make the myNamespace object implicit and not specified in the namespace parameter call because it seems less redundant. The code that does this is fairly forward and lives in the loop on lines 5-12 and the return statement on line 16.

The other thing that I've done is added an optional function parameter to the namespace object which will automatically be called on the newly created namespace (line 14). I will explain the idea through examples.

Using This Namespace Object
The simplest way to use this is to just add your variables to the namespace object. i.e. rather than having code in global namespace like:
var x = 5; 
function sort(arr) { /* code */ }
you do something like:
myNamespace.x = 5; 
myNamespace.sort = function(arr) { /* code */ }
or you can even namespace it further:
myNamespace.util.sort = function(arr) { /* code */}
As you can see, by doing this the only global variable you have created is the myNamespace object and everything else is a property of it.

So what's with the optional function parameter? It lets you define an entire namespace all at once and even allows the idea of private variables and functions from the Module Pattern. Here's an example:
  1 myNamespace.namespace("util.collection", function() { 
  2   var InternalType = function(){/*class that is only used by this namespace*/}
  3   InternalType.prototype.sort = function() { /* code */ } 
  5   var Stack = function() { /* code */ }
  6   Stack.prototype.push = function(value) { /* code */} 
  7   // etc. 
  9   this.Stack = Stack;  // make the Stack type public in this namespace
 10 });
Basically lines 2-8 are the internal namespace definition. Anything that gets added to the this variable (like Stack on line 9) become the public part of the namespace. Once we've defined the namespace we can then use the public classes and variables in it.
var stack = new myNamespace.util.collection.Stack();
And, of course, we can use one namespace in another namespace:
  1 myNamespace.namespace("", function() { 
  2   var Stack = myNamespace.util.collection.Stack;  // similar to a java import
  4   var doSomething = function(x) { 
  5     var stack = new Stack();
  6     stack.push(x); 
  7    /* do more stuff */ 
  8   } 
 10   // define public aspects of this namespace
 11   this.doSomething = doSomething; 
 12 });
One thing I want to point out is the Stack variable on line 2. Fully qualifying everything makes for unwieldy code which is why Java has import statements and C# has using statements. Since the namespaces and the classes are all really just objects, you can create your own variables with shorter names to refer to the fully qualified object. As long as you do this inside a function (for example the anonymous function being passed to the namespace method), the Stack variable is not polluting the global namespace.

Take Home Message

Make sure you respect the global namespace.

Whether you use the function and internally scoped objects or not, the important thing is that the only variable added to the global namespace is myNamespace. As such it is easy to avoid collisions with any other libraries, just change the name "myNamespace" to something that is unique to your code. This allows you to play nice with others and not worry about 3rd party apps messing up your functionality.

No comments: