Namespacing JavaScript
One of the biggest drawbacks in JS is the lack of proper namespaces. Some have long used objects to avoid global pollution, but I think it's possible to go further. That's how I came with those two functions :
bundle = function (name, func) {
var cur = global;
var name_arr = name.split('.');
for (i = 0, ilen = name_arr.length; i != ilen; i++) {
var n = name_arr[i];
cur = cur[n] = cur[n] || {};
}
if (func) func.call(cur);
};
fetch = function(imports, func) {
var ob = {};
var inject_bundle = function (name, ob) {
var name_arr = name.split('.');
var cur = eval(name_arr[0]);
for (i = 1, ilen = name_arr.length - 1; i != ilen; i++) {
var n = name_arr[i];
cur = cur[n];
}
var last = name_arr[name_arr.length - 1];
if (last == '*') for(k in cur) ob[k] = cur[k];
else ob[last] = cur[last];
}
if (typeof imports == 'string') inject_bundle(imports, ob);
else for (k in imports) inject_bundle(imports[k], ob);
return func.call(ob);
};
})(this)
bundle is used to create a new namespace, given as a string. If a function is passed as a second argument, then it is called, bounded to the namespace. For example, one could use the following :
this.bleh = "I'm a turtle !";
});
bundle('net.friggeri.quux', function () {
this.a = 32;
this.b = 52;
});
print(net.friggeri.foo.bar.bleh);
print(net.friggeri.quux.a * net.frigger.quux.b);
You might think (and you'd be right) that having long names can be a pain (I would never use something like net.friggeri.quux.a). However, using the fetch function, you can populate an object with whatever slots in a given namespace. Then, using with, you can emulate some kind of pythonish import statement. An example might be useful at this point:
'net.friggeri.foo.bar',
'net.friggeri.quux.*'
],
function () { with(this) {
print(bar.bleh);
print(a*b);
}})
The code is pretty ugly: I have to use eval at some point to make sure it still works if called in a nested function and I didn't find a way to avoid using with in the function passed to fetch. Of course it's possible to use a this.bar.bleh syntax, but the whole point of fetching the contents of the bundle is to avoid using such long variable names.
If you wish to expand this, or have any suggestions, you're welcome to comment.
Update : I just realized that it could be simplified a lot with having fetch returning directly the object, allowing something like :
'net.friggeri.foo.bar',
'net.friggeri.quux.*'
]) {
print(bar.bleh);
print(a*b);
}
This is a very good example where having an all functional approach might not be the path to follow. ![]()
5 Responses to "Namespacing JavaScript"
"One of the biggest drawbacks in JS is the lack of proper namespaces."
It seems to be a problem, especially when coming from languages with different means of managing the global namespace. But JavaScript has its own abilities. Closures and using a global "namespacing" objects are one technique for public and private members of "packages". Macromedia had a system of using underscores MM_someFunction. This gives just as much namespace colission protection as what you are suggesting. In some browsers it is slower to have many global variables. In other browsers it is faster than the two level property look-up of MM.someFunction.
I think that whatever language is being used, it is very important to use the language the way that particular language works best. JavaScript doesn't have packages (although it will in ECMAScript4) so at least for now the simple solutions yield faster code and still do protect against global namespace collisions. And if that is accomplished then there really is not need to worry as the goal has been achieved.
Actually, this was a first draft of something I wished to see in xjs. I agree that in a browser context, there is very little need to support a whole lot of different packages living in the same environment. However, I'm pretty sure than in a broader context (such as xjs), a packaging system such as this one is a necessity.
The system I suggest is not far from what is currently used, I just provide easy functions to create/open a package. The huge advantage over using _ is that it allows the opening of just one part of a package.
I don't think a packaging system beyond what JavaScript provides naturally is necessary. Avoiding global namespace collisions is easy and fast by either using the macromedia style (e.g. MM_someFunction) or using a object (e.g. MM.someFunction). If you want more elaborate namespacing you could use net_friggeri_someFunction or net_friggeri.someFunction or if even slower performance is desired net.friggeri.someFunction. These all "solve" the problem simply and are all reasonably efficient. Which problems do these simple techniques not solve?
I think you misunderstood what I was saying. I totally agree that these simples techniques solve every imaginable problem. What I'm suggesting here is, instead of going through the trouble of defining your object net.friggeri.foo, and then manually defining net.friggeri.foo.someFunction, net.friggeri.foo.someOtherFunction, you can use a more concise way of writing it.
Same thing for fetch, it just gives the ability to "open" a package, avoiding the necessity to type the whole package name everytime.
I'm not trying to reinvent the wheel here
So you are after a simpler way to define a serious of nested namespace objects. Something that will make intermediate objects as needed. Like YAHOO.namespace
http://developer.yahoo.com/yui/yahoo/#namespace
or even similar to UNIX's mkdir -p.
That is a fine idea to save the labor of checking if each level of namespacing is already defined and then defining the level if it is not. You could easily make an xjs module with your code. I would caution strongly against the use of JavaScript's "with" as it causes virtual chaos.
The main problem is how many levels of namespacing are required vs the performance hit? Using more than one level of object namespacing seems like an unecessary performance hit when underscores do the same job with respect to namespacing as periods do. They are just characters after all. Using pure underscore namespacing is sufficient also and in my very casual tests with Rhino it didn't seem to be a problem to have many global objects. In fact, if that is something you would like to investigate in detail I would be very interested in reading your results for accessing namespaced objects using different namespacing schemes.
Leave a Reply