A View Inside My Head

Jason's Random Thoughts of Interest

NAVIGATION - SEARCH

Namespacing in JavaScript

Consider this code:

var Configuration = function (model) { 
   this.name = ko.observable(model.Name); 
   this.description = ko.observable(model.Description); 
   this.enabled = ko.observable(model.Enabled || false); 
}; 

Because this was defined outside of the scope of a function, that “Configuration” variable gets promoted to the global object.  In web browsers, that means that it gets appended to the window object as a property (window.Configuration = function(model)…). 

That’s just the nature of global objects in JavaScript.  But, instead of creating a bunch of new custom properties on window, it’s common to wrap all of your app’s global objects in a namespace so that window only gets polluted by one new property (especially when creating a reusable library of objects).

var MyNS = MyNS || {};         

///        
/// Configuration        
///        
MyNS.Configuration = function (model) {            
   this.name = ko.observable(model.Name);            
   this.description = ko.observable(model.Description);            
   this.enabled = ko.observable(model.Enabled || false);        
};

Now, in reality, there’s no such thing as a namespace in JavaScript.  What this is actually doing is creating a new global object called “MyNS” if it doesn’t already exist, and then adding the new objects as properties of MyNS.

If you need a sub-namespace, just create another empty object in MyNS:

var MyNS = MyNS || {};        
MyNS.Entities = MyNS.Entities || {};

Now the namespace and its members can be extracted into their own .js file(s) and included when needed.

But, thinking about a shared usage, someone may want to instantiate a Configuration object without having a model to pass in.  In that case, the code above will blow up because there are no properties on undefined (i.e., undefined.Name would result in an error).  This can be fixed by initializing model itself to an empty object if it comes in undefined:

MyNS.Configuration = function (model) {            
   model = model || {};            
   this.name = ko.observable(model.Name);            
   this.description = ko.observable(model.Description);            
   this.enabled = ko.observable(model.Enabled || false);        
};

But, wait – there’s no .Name property of an empty object.  How does this work if model = {} ?

All object in JavaScript are actually associative arrays (think dictionary, property bag, etc).  So, model.Name is exactly the same as model["Name"].  If that named element doesn’t exist in the collection, then the value will be returned as undefined, which is fine for initializing a ko.observable.

Additional Suggestion

Jason Karns (@jasonkarns) writes:

I like to use a micro-library, extend.js, to make the namespace initialization a bit cleaner. https://github.com/searls/extend.js It's a tiny library that does, admittedly, very little.

When breaking components across multiple files, ensuring that the namespace (and sub-namespace) is already created becomes annoying. You either end up declaring a very specific file load order, or copying boilerplate namespace code at the top of every file.

extend.js let's you define the namespace you want as a string (to any depth) and it will create it if it doesn't exist, or merge if it does exist. Small utility, but much cleaner than boilerplate code.