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