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!
kangax said:
#As Nicolás Sanguinetti pointed out, we could remove the temporary o variable and return object explicitly. The function becomes ultra concise:
String.prototype.namespace = function(separator) {
this.split(separator || '.').inject(window, function(parent, child) {
return parent[child] = { };
})
}
insane said:
#wow! I was thinking on js namespaces just a couple of days ago Thnks.
Tim said:
#I'll take the explicit namespacing over this approach because the readability of the former is significantly better than the latter. Most sites/developers can keep their namespace fairly shallow as well which reduces the typing.
Michael Lee said:
#Interesting read; novel way of implementing javascript namespaces ;-)
I can see why you chose to extend the String object, but I'm still wary of modifying core object prototypes...That said, the simplicity of your solution is definitely appealing and insightful.
Not sure how serious/interested you are in the JavaScript namespacing topic, but I'd be interested to hear your thoughts on Ajile -: http://ajile.iskitz.com/. It's my stand-a-lone solution to JavaScript namespace handling and its related concepts like importing and handling ambiguity.
henrah said:
#you could replace
return parent[child] = { };with
return parent[child] = parent[child] || { };To avoid overwriting existing objects. For example, if
window.foois already defined, calling'foo.bar.baz'.namespace()will add the.bar.baznamespace without destroying any other properties onfoo.buggedcom said:
#checking if a namespace exists
String.prototype.namespaceExists = function(){
var parts = this.split('.'), exists = true, i=0, base=window, l=parts.length;
while(i
buggedcom said:
#hmmm... didn't like that...
String.prototype.namespaceExists = function()
{
var parts = this.split('.'), exists = true, i=0, base=window, l=parts.length;
while(i
buggedcom said:
#didn't like that either.... oh well
Nikso said:
#Here is my version:
String.prototype.namespace = function(source) {
Object.extend(this.split('.').inject(window, function(parent, child) {
return (parent[child] = parent[child] || { });
}), source || { });
}
That allow:
"Ns.Test".namespace({
Nested: {
Str: "ya!"
},
Cls: Class.create({
name: "Nested class ",
say: function(what) {
alert(this.name + what);
}
})
});
Can be called like:
(new Ns.Test.Cls).say(Ns.Test.Nested.Str);
bb ^_^
Mark A. Ziesemer said:
#Addresses many of the comments made above and elsewhere, and doesn’t require inject: