JavaScript Namespacing
There are many ways to safely namespace your objects in JavaScript. This article discusses common practices I have seen.
Prefix Namespacing
If the goal of namespacing is to avoid collisions. The following system is just as likely to avoid namespace collisions as any other system as long as we know the global variable name prefix myApp_
is unique.
// add uniquely named global properties
var myApp_sayHello = function() {
alert('hello');
};
var myApp_sayGoodbye = function() {
alert('goodbye');
};
// use the namespace properties
myApp_sayHello();
C programs frequently use prefix namespacing. In the JavaScript world, you may have encountered Macromedia's MM_
functions like MM_showHideLayers
.
I think that prefix namespacing is the most clearly understandable namespacing system in JavaScript. (The object namespacing strategies below can cause confusion when the keyword this
becomes involved.)
Prefix namespacing does create many global objects. This is not a problem in regard to namespace collisions as the prefix protects against that. The problem with prefix namespacing is some web browsers (e.g. Internet Explorer 6) perform poorly if there are many global objects, so I am told. I ran some tests and there was a little thread on comp.lang.javascript but I didn't peruse this topic completely.
Single Object Namespacing
Currently, the most popular JavaScript namespacing practice is using a single global variable to reference an object. That referenced object then references your "real business" and because your global object is named uniquely then your code should run happily with anyone else's code present.
If you are pretty sure no one in the world has used the global variable name myApp
then you may have code like this
// define the namespace object
var myApp = {};
// add properties to the namespace object
myApp.sayHello = function() {
alert('hello');
};
myApp.sayGoodbye = function() {
alert('goodbye');
};
// use the namespace properties
myApp.sayHello();
When the the last line of code above runs, the JavaScript interpreter first finds the myApp
object, then finds and calls that object's sayHello
property.
A problem with object namespacing is it leads to confusion with object-oriented message passing. There is no obvious syntactic difference between
namespace.prop();
and
receiver.message();
Looking at this confusion more carefully we have the following for the namespace concept. Suppose we have the following library.
var myApp = {};
myApp.message = 'hello';
myApp.sayHello = function() {
alert(myApp.message);
};
The code using this library is free to write.
myApp.sayHello(); // works
var importedfn = myApp.sayHello;
importedfn(); // works
Compare this to the confusing message passing version which uses this
.
var myApp = {};
myApp.message = 'hello';
myApp.sayHello = function() {
alert(this.message);
};
The code using this library is free to write.
myApp.sayHello() // works because "this" refers to myApp object.
var importedfn = myApp.sayHello;
importedfn(); // error because "this" refers to global object.
The lesson to be learned here is that this
should never refer to an object being used as a namespace because it leads to confusion about importing identifiers from that namespace. This problem is one of the reasons that this
makes my list of JavaScript Warning Words
(This also suggests that a library's API properties should point to the same function so that these functions can be imported into other spaces. This problem was pointed out in the comments of my Lazy Function Definition Pattern article. The lazy function definiton can be used safely when hidden within a library and is not part of the API.)
Nested Object Namespacing
Nested object namespacing is another common practice which extends the idea of object namspacing. You may have seen code like the following
YAHOO.util.Event.addListener(/*...*/)
Resolving the above line requires the interpreter to first find the global YAHOO
object and then its util
object and then its Event
object and then find and call its addListener
property. This is way too much work each time an event handler is attached to a DOM element so the concept of importing is used.
(function() {
var yue = YAHOO.util.Event;
yue.addListener(/*...*/);
yue.addListener(/*...*/);
})();
If you know that the YAHOO.util.Event.addListener
function does not use the this
keyword and always references the same function then the importing could be even more compact.
(function() {
var yuea = YAHOO.util.Event.addEventListener;
yuea(/*...*/);
yuea(/*...*/);
})();
I think nested object namespacing is unnecessarily complex when the goal is simply avoiding identifier collisions. Is Yahoo! not confident that the global identifiers YAHOO_util_Event
and YAHOO_util_Event_addEventListener
are unique?
I think the motivation for nested object namespacing is to have the appearance of the Java package
naming convention which in Java is inexpensive. For example, in Java you may see the following.
package mytools.text;
class TextComponent {
/* ... */
}
A fully qualified reference to the class above would be mytools.text.TextComponent
.
The following package naming description is in Learning Java by Niemeyer and Knudsen.
Package names are constructed hierarchically, using dot-separated naming convention. Package-name components construct a unique path for the compiler and runtime systems to locate files; however, they don't create relationships between packages in any other way. There is really no such think as a "subpackage"; the package namespace is, in actuality, flat—not hierarchical. Packages under a particular part of the package hierarchy are related only by convention. For example, if we create another package called
mytools.text.poetry
(presumably for text classes specialized in some way to work with poetry), those classes won't be part of themytools.text
package; they won't have the access privileges of package members.
The illusion of nested namespaces in Perl exists also. In Perl, the nested package names are separated by double colons. You may see Perl code like the following.
package Red::Blue;
our $var = 'foo';
A fully qualified reference to the above variable would be $Red::Blue::var
.
In Perl, like Java, the idea of a namespace hierarchy is only for the benefit of the programmer, not the language. Programming Perl by Wall, Christiansen and Orwant explains
The double colon can be used to chain together identifiers in a package name:
$Red::Blue::var
. This means the$var
belonging to theRed::Blue
package. TheRed::Blue
package has nothing to do with anyRed
orBlue
packages that might happen to exist. There is, a relationship betweenRed::Blue
andRed
orBlue
may have meaning to the person writing or using the program, but it means nothing to Perl. (Well, other than the fact that, in the current implementation, the symbol tableRed::Blue
happens to be stored in the symbol tableRed
. But the Perl language makes no use of that directly.)
The parenthetical note at the end of the above quotation implies that Perl may have the same identifier resolution cost that using nested namespace objects in JavaScript has. If the Perl implementation changes the cost could disappear. In JavaScript, I believe that the cost of nested object namespacing will never go away because JavaScript uses late-binding.
I don't think nested object namespacing in JavaScript provides any significant benefit but it is potentially very expensive at runtime if importing is not used.
A compromise
If pure prefix namespacing really is slow in some browsers, and the concept of nested namespaces helps keep things organized in the developer's mind then I think the Yahoo! example above could be written as either
YAHOO.util_Event_addListener
or with a few more globals (but still not many for any give page)
YAHOO_util_Event.addListener
Namespacing in which dimension?
Perl CPAN modules are namespaced based on what they do. For example, I wrote a module in the namespace
JavaScript::Minifier
If someone else writes his own module with the same name, and he unknowingly uses the CPAN module by the same name through some module dependency, then a collision occurs.
Java programmers takes the most verbose, but safest, approach, of course. (Java programmers seem to think about code running in huge systems.) In Java, packages are generally namespaced based on both who wrote it and what it does. (A formalization of the myFunc
style.) The "who wrote it" part even uses a relatively guaranteed unique name that the developer "owns". If I wrote a Java minifier, it would use the following namespace since I own the michaux.ca domain name.
ca.michaux.javascript.minifier
In JavaScript, after the results of this discussion, this could be written more efficiently as
ca_michaux_javascript_minifier
Because JavaScript is served as text, this sort of namespacing seems too expensive by increasing download time. Gzip compression finds common strings and replaces them with a small string. If gzip is not available then importing could be used.
var ca_michaux_javascript_minifier = {};
(function() {
var cmjm = ca_michaux_javascript_minifier;
// refer to cmjm inside this module pattern
})();
I'm not suggesting that these long namespaces are necessarily necessary but they are definitely the safest in avoiding namespace collisions.
Other Namespacing Issues
Identifiers are not only created in JavaScript source. The name attribute of a form is added to the document.forms
object. It makes sense to namespace these with <form name="myCompany_login">
.
Namespacing class name attributes like <div class="myCompany_story">
could be worthwhile also to reduce CSS namespace collisions and when JavaScript code is searching for DOM elements by class name.
Summary
Personally I think that anything like YAHOO.util.Event.addListener
, with dots or underscores, is overly paranoid of collisions. It could have been just YUI.on
. Dojo provides enough protection with dojo.connect
for the same job because it covers the "who" and "what" dimensions of namespacing sufficiently. No one in their right mind is going to come along and write a JavaScript library under the namespace dojo
. The Dojo developers will not forget they already have a connect
function and write another one.
It might be good if we had a website where programmers could reserve their JavaScript globals identifiers, underscore prefixes and when ECMAScript 4 is released also their package names: "The JavaScript Namespace Registry".
JavaScript is a powerful language with a minimal set of concepts. Even though JavaScript doesn't have language-level support specifically designed for avoiding namespace collisions, there are several ways to solve the problem. There is no one "right" answer. Pick the system you like best.
But please, whatever you do, just don't put another global $
identifier out there!
Extras
comp.lang.javascript discussion on namespacing
Update August 9, 2008 The Linux folks have a namespace registry: LANANA.
Comments
Have something to write? Comment on this article.
kangax,
I ran the following three tests separately in Firefox 2. I've found that running speed tests in the same execution favors the second test.
I don't call the function in these tests because namespacing is more about resolving the identifiers rather than what happens after they are resolved.
I've added an empty loop test as a control.
var foo={bar:{baz:{qux:{flux: function(){ return 5; } }}}};
(function(){
var s = new Date();
for (var i=1e6; i--; ) {
foo.bar.baz.qux.flux;
}
var e = new Date();
alert(e.getTime()-s.getTime()); // 530 ms
})();
---------
var moo=function(){ return 5; };
(function(){
var s = new Date();
for (var i=1e6; i--; ) {
moo;
}
var e = new Date();
alert(e.getTime()-s.getTime()); // 340 ms
})();
---------
(function(){
var s = new Date();
for (var i=1e6; i--; ) {
}
var e = new Date();
alert(e.getTime()-s.getTime()); // 60 ms
})();
Now compare the resolution times of the first two and the difference is quite pronounced.
(530 - 60) / (340 - 60) = 1.68
A million resolutions is many. Some complex event handlers may make this many resolutions but it is not the common case. When discussing something like performance, the common cases are often not the concern but rather the extremes are of interest. This article wasn't intended to say that the nested namespacing is not viable. It is being used on many pages; however, there is no real benefit to using it over a scheme like the compromise I described in the article.
Peter, great article. Namespacing in Javascript is something that I've always been a little hesitant to go into and this showed me some important things. I like the issues you raised with the nested notation, its something I never really considered.
To add to the benchmarks, I ran it on my FF3rc3, Mac OS X 10.5.3 and the difference was actually worse then both of yours. The nested ranging from 1.7 to over 3 times worse. Any idea why the tests could have so much variance?
Joseph,
Sometimes I get a particularly slow run when testing but then again, sometimes I'm typing on my computer and it just freezes for a second or so. Time slicing? Garbage collection? It is hard to say.
Hey Peter,
Nice write-up. Can't say that I agree with you completely, but you've made some good points and did end by saying "Pick the system you like best" , fair enough :-)
Javascript Namespacing is one of my passions, strange perhaps, but true. It's been the main motivation behind my creation of Ajile (Advanced JavaScript Importing & Loading Extension).
I currently prefer object based namespacing specifically that using reverse domain names. That said I only believe that level of verbosity is needed when working in an open environment like the web. Here are my top 3 reasons for preferring reversed-domain object-based namespacing:
1. I find object oriented (dot notation) code easier to read and manage. The encapsulation that comes with object-based namespacing allows me to filter functionality both visually at design time and actually at run time. Being able to easily see and request/use what I need has proven to be as important as being able to ignore what I don't.
2. I develop for the web. As you know, it's a large interconnected system (of systems). If ever there was a place where encapsulation and unique identification was needed, it's here. The human readable domain names we use to encapsulate and identify content on the web has proven to be quite effective and so I believe the reverse domain namespacing approach will also be effective in this arena.
3. The web seems to be evolving from disparate sources of content and functionality statically linked, to a dynamic medium where multiple sources interact and exchange information on a regular basis. Being able to confidently and uniquely identify content and functionality is becoming evermore important with the emergence and rapid growth of mashups, widgets, and social networking especially now that they're becoming integrated into our daily lives and the tools we rely on. Reverse domain name object namespacing is a first step towards an assurance of functionality and content ownership. All that's needed after that is enforcement...
I haven't done benchmarking against sites using object-based namespacing, but as you've said importing negates the performance issue, and that's one of the key features I've built into Ajile
Keep the interesting topics coming, more often than not your posts catch my eye, keep it up ;-)
I haven't done any tests and I'm just speculating here, but what are the performance differences between:
1) Many functions in the global namespace. e.g., MM_run1(), MM_run2(), MM_run3(), etc. For any and every function call (direct or indirect), the JS engine must sift through all those global functions before it finds the one it needs. Will even calling alert(), slow down the engine because of too many functions in the global scope?
2) All functions hidden behind 1 global object. e.g., dojo.run1(), etc: If calling a normal non-namespaced function (e.g., alert), will it be fast because there is less in the global scope for the JS engine to sift through?
3) Perhaps if all the global functions are stored internally by the JS engine as a hashtable, then the cost of lookup is cheap, but if they are stored as a linked-list, then it would obviously be expensive and hiding all functions behind single global objects would be more efficient?
Nathar,
There have been performance problems of too many global variables in the past. A few things have changed.
The most important change is that using the module pattern, not many things need to be in the global namespace as many functions can be hidden in closures. Even on some very big pages, I only have about 40 global symbols and that isn't many.
Another option is to make a local reference inside a closure to what would otherwise need to be referenced through a global variable. This technique is actually recommended when using objects as namespaces because looking up properties of an object has cost as well.
I don't think anyone would implement a symbol table as a linked list. A hash is the natural way to go.
Great article. Here is an article on how to use it with the jQuery object. In other words namespasing with the jQuery.
Interestingly, accessing the object members with strings suffers a performance penalty in Safari 4.
Looping 1 million times:
foo.bar.baz.qua.flux() | foo["bar"]["baz"]["qux"]["flux"]() | moo() | |
---|---|---|---|
Safari 4 | 229ms | 311ms | 139ms |
Firefox 3.0 yields identical results for both forms | 2626ms | 2626ms | 1906ms |
Safari 4 is nearly 10x faster on this test.
I just found this post. I use a variant of hierarchical namespaces that does not require a specific load order:
I’ve found it quite useful in building portable modules that I can use between projects.
Have something to write? Comment on this article.
I ran a little test to see what the actual performance difference is when using global variable vs. a "nested" one. It seems to be somewhat tiny (FF 3rc2, Mac OS X):