Perfection Kills

by kangax

Exploring Javascript by example

← back 414 words

Namespacing made easy

Enumerable#inject is one scary method. It took me a while to understand the beauty of this functional programming paradigm. Just in case you were ever wondering about its real life usage – here’s one of them.

We all know that using namespaces is generally a wise thing to do. You don’t have to create a multi level object structure, but nesting your code under a pseudo-namespace is what makes maintenance, upgrades and migrations somewhat easier and error-prone.

Few popular javascript libraries implement some sort of a utility function that automatically creates nested objects. What it means is that instead of writing it manually, you could let the helper handle it for you:


// creating nested structure manually is painful
var com = {
  thinkweb2: {
    projects: {
      _prototype: 'a deeply nested property...'
    }
  }
};

com.thinkweb2.projects._prototype // => 'a deeply nested property...'

Well, it appears that inject can handle such task in only 3 lines of code:


'com.thinkweb2.projects.prototype'.split('.').inject(window, function(parent, child) {
  var o = parent[child] = { }; return o;
});

Object.inspect(com.thinkweb2.projects.prototype); // => "[object Object]"

Not bad – a completely generic namespacing snippet.

What’s happening here is not a black magic. Inject accepts initial object as a first argument (in this case – window object) and iterator function as the second argument. It then calls iterator function on each item from the collection (in our case it’s simply a string split into an array). Iterator function receives accumulated result as a first argument and current value as a second. Iterator performs an action (creates a nested object) and returns accumulated result (the newly created parent object) to be used in following iterations.

Alternatively, we could wrap this all nicely and define as a String.prototype method:


String.prototype.namespace = function(separator) {
  this.split(separator || '.').inject(window, function(parent, child) {
    var o = parent[child] = { }; return o;
  });
};

foo.bar.baz.quux = 'Nothing special...'; // => ERROR: foo is not defined

// Default separator is '.'
'foo.bar.baz.quux'.namespace();

foo.bar.baz.quux = 'Nothing special...'; // => "Nothing special..."

// Or using a custom one
'MY_AWESOME_APPLICATION::util::DOM::dimensions'.namespace('::');

'dimensions' in MY_AWESOME_APPLICATION.util.DOM; // => true

Note how we use “separator” argument (or defaulting to ‘.’) making the function somewhat more flexible.

P.S.
Heavy namespacing could lead to unnecessary complexity.
Lack of it – to buggy behavior.
In the end, the only thing that matters is what works best for you.

Enjoy!

Did you like this? Donations are welcome

comments powered by Disqus