May 2008 Archive

Enliven your namespaces

By Luke Smith on May 1, 2008 9:59 AM

It's a common good practice to use namespaces when writing your JavaScript. The reasons why have been stated time and again by much smarter and more eloquent people than myself, so suffice it to say, you should do it.

The typical method for namespacing has been to use object literals such as

var myNS = {
    myFunc : function () { /* do stuff */ }
};

However, A namespace needn't be inert1. It's just an object to hang stuff off of, so technically it can be anything that you can hang stuff off of. That is, anything that descends from Object, which in JavaScript is just about everything, including things like RegExp instances and, more interestingly, functions.

Yay lambda

In JavaScript, functions are first class objects. This allows code such as this:

var foo = {
    bar : function (m) {
        alert(m);
    }
};

var barFunc = foo.bar;
var baz = {
    bar : foo.bar,
    goop : barFunc
};

// All of these will now do the same thing
foo.bar('Hello world');
barFunc('Hello world');
baz.bar('Hello world');
baz.goop('Hello world');
foo.bar.call(someRandomObject, 'Hello world');

The only difference between them is the resolution of the this keyword.

Breathe life into your namespace

Let's use a function as a namespace instead. Let's say you have an object literal containing a suite of functions, but one function is called far more often than the others. We can use that function as the namespace, then alias the function name from within the namespace for consumers that like to type.

// convenience function for merging objects
var set = function (to,from) {
    for (var k in from) {
        if (from.hasOwnProperty(k)) {
            to[k] = from[k];
        }
    }
};

// create a namespace called node (in practice, choose something better)
var node = function (id) {
    return document.getElementById(id);
};

// Add functions to the function namespace just as you would to an object literal namespace
set(node, {
    byId : node, // alias to NS function for completeness

    /* ... and then a few more functions that sit in the node namespace */
    _templates : {},
    create : function (name,conf,content) {
        var n = (this._templates[name] || (this._templates[name] = document.createElement(name));

        if (n) {
            n = n.cloneNode(true);
            set(n,conf); // set things like className or innerHTML
            if (content) {
                n.innerHTML = n.innerHTML.replace(/{([\w\-\s]+)}/g,function (x,placeholder) {
                                         return (placeholder in content) ? content[placeholder] : '';
                                     });
            }
        }

        return n;
    },
    remove : function (n) {
        if (n.parentNode) {
            n.parentNode.removeChild(n);
        }
    },
    /* etc etc */
});

// Now you can use the namespace node(x) rather than type node.byId(x)
var n = node('foo');
var n1 = node.byId('foo'); // synonyms

// And of course, the rest of the functions work the same
var div = node.create('div',{className: 'bar', innerHTML: '<p>{message}</p>'}, {message: 'hello world'});

this()

An interesting side effect of using a function as a namespace is that during the execution of functions under that namespace, this is resolved to the function ref. That means you can do something like

var node = function (id) {
    return document.getElementById(id);
};
node.remove = function (id) {
    var n = this(id);

    if (n && n.parentNode) {
        n.parentNode.removeChild(n);
    }
};

The use of this as a function ref in that case would cause a JavaScript error in the case where the node.remove function was not called from the node context such as in

var rm = node.remove;
rm('boom'); // JavaScript error.  'this' evaluates to the global object (window) which isn't a function

// or
node.remove.call(somethingOtherThanAFunction,'boom');

But that's a separate issue. The language has the capacity to be used in this way, and the value of leveraging this tactic needs to be evaluated and contrasted to the the potential breakages.

I'm personally a big fan of function namespaces, but it hadn't occurred to me until just this morning that this also gained some super powers in the process. I haven't yet thought of a case for its use (other than to trim code), but boy am I interested to hear suggestions!

  1. Actually, even the object literal namespace isn't inert. It brings with it all the methods and members that hang off of Object instances.

ls.n

LucasSmith.name

Luke and Liam

I'm Luke. I am a front end engineer at Yahoo! on the YUI team.

Mostly I write about code stuff, but occassionally I'll mix in some real life. You've been warned.

Archives

Tags

Feeds

Subscribe to feed Recent entries

Content licensed under Creative Commons

Code licensed under BSD license

©2005 - 2010 Lucas Smith

Powered by Movable Type