Suggestion for Emulating Namespaces in JavaScript
I’ve been giving some thought on how to emulate namespaces in JavaScript (at least until they’re implemented in the language), and this is what I’ve come up with: use objects for namespaces. This was inspired by a glance at the prototype, where I noticed that Sam Stephenson was using objects to group related things into neat packages.
For example, say that you wanted to create a class for managing music on you
CDs. Normally in JavaScript, you’d create a class named CDMusic
. This is all
well and fine, but if everyone creates classes with a single name, a JSAN
repository would end with an awfully crowded list of classes. It allows for no
effective hierarchical organization of code.
But if you use objects to represent namespaces, you can define a class something like this, instead (1990s-era example borrowed from Damian Conway’s Object Oriented Perl):
if (CD == undefined) var CD = {}; // Make sure the base namespace exists.
CD.Music = function () {}; // Constructor definition.
// Class definition.
CD.Music.prototype = {
name: null,
artist: null,
publisher: null,
isbn: null,
tracks: [],
location: function (shelf, room) {
if (room != null) this._room = room;
if (shelf != null) this._shelf = shelf;
return [this._room, this._shelf];
},
rating: function (rate) {
if (rate != null) this._rating = rate;
return this._rating;
}
};
So now, to use this class, you just:
var music = new CD.Music();
music.name = "Renegades";
music.artist = "Rage Against the Machine";
music.tracks.push("Microphone Fiend");
music.location("basement", 3); // I use an iPod, so it's in storage!
Of course, the key part of this example is var music = new CD.Music();
. Note
how the class is defined as an attribute of the CD
object. This allows us to
have a namespace, CD.Music
, that is subsumed under another namespace, namely
CD
. The nice thing about this is that, in the hypothetical JSAN repository,
the class might be defined the file Music.js in the CD directory. A use()
function as described by Michael Schwern might then be smart enough to
look for /use/CD/Music.js when you write use("CD.Music")
.
I also kind of like how the use of the namespace and prototype allows my class
definition to be indented by the creation of the prototype object. But to make
these types of namespaces work, you must have that first statement:
if (CD == undefined) var CD = {};
. This allows you to assign to a CD object
whether you have to create it (because your class stand on its own), or because
some other JavaScript class has defined it. This is especially important to
ensure that you don’t stomp on someone else’s work. Say someone is using two
different JavaScript classes, your CD.Music
and someone else’s CD.Jukebox
.
The two classes might be completely unrelated to each other, but because they
both define themselves under the CD
top-level namespace using the
if (CD == undefined)
statement, they won’t stomp on each other.
The only downside to this proposal, in my estimation, is the requirement it
imposes for defining inherited classes. Say you wanted a subclass of CD.Music
for classical music. You’d have to do it like this:
// CD.Music must be loaded already. Create the constructor.
CD.Music.Classical = function () {}
// Inherit from CD.Music.
CD.Music.Classical.prototype = new CD.Music(); // Inheritance.
// Add to the class and/or override as necessary.
CD.Music.Classical.prototype.composer = null;
CD.Music.Classical.prototype.orchestra = null;
CD.Music.Classical.prototype.conductor = null;
CD.Music.Classical.prototype.soloist = null;
So we don’t get the block syntax, but in truth, that’s no different from how one typically handles inheritance in JavaScript. The only difference is the use of the dot notation. Nevertheless, suggestions for how to use a block syntax would be warmly received.
So what do you think? Is this something that makes sense to you? Would you do it
to better organize your JavaScript classes and modules (and yes, I am thinking
that you could group functional libraries this way, too, and then implement an
import()
function to export functions to another “namespace” or the global
object)? Leave your opinions in a comment. Thanks!
Looking for the comments? Try the old layout.