Perfection kills

Exploring Javascript by example

Namespacing made easy

January 30th, 2008 by kangax

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!

Categories: inject

Comments (14)

  1. Gravatar

    kangax (article author) 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] = { };
    })
    }

  2. Gravatar

    insane said:

    wow! I was thinking on js namespaces just a couple of days ago Thnks.

  3. Gravatar

    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.

  4. Gravatar

    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.

  5. Gravatar

    henrah said:

    you could replace
    return parent[child] = { };
    with
    return parent[child] = parent[child] || { };

    To avoid overwriting existing objects. For example, if window.foo is already defined, calling 'foo.bar.baz'.namespace() will add the .bar.baz namespace without destroying any other properties on foo.

  6. Gravatar

    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

  7. Gravatar

    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

  8. Gravatar

    buggedcom said:

    didn't like that either.... oh well

  9. Gravatar

    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 ^_^

  10. Gravatar

    Mark A. Ziesemer said:

    Addresses many of the comments made above and elsewhere, and doesn’t require inject:

    
    String.prototype.namespace = function(separator){
      var ns = this.split(separator || '.')
      var o = window;
      for(var i=0, len=ns.length; i<len; i++){
        o = o[ns[i]] = o[ns[i]] || {};
      }
      return o;
    };
    
    
  11. Gravatar

    jQuery How to said:

    Great article. Here is an article on how to use it with the jQuery object. In other words namespasing with the jQuery http://jquery-howto.blogspot.com/2009/01/namespace-your-javascript-function-and.html

Trackbacks

  1. JavaScript namespaces said:

    [...] on the Prototype namespacing made easy, except this doesn’t require Prototype. String.prototype.namespace = [...]

  2. eric.polerecky.com » Blog Archive » links for 2008-02-05 said:

    [...] perfection kills » Blog Archive » Namespacing made easy Perfection kills. Prototype javascript framework tutorials and examples, tips and tricks on modern front-end web development (tags: javascript prototype) [...]

  3. JavaScript Namespacing in 85 Characters said:

    [...] but only once, for our top level namespace object. This is far from a new idea. Some examples build namespacing using PrototypeJS (or one from Mark Ziesemer), some use the Yahoo API for namespaces. All of these have in common the [...]

Leave a Comment

Allowed tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>

Please, don't forget to escape your input (<, > and &). Wrap code sections with <pre>