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!

Backtalk

:-$ wrote:

js namespaces

Birdman's land wrote:

JS Namespaces redux

Bob Ippolito wrote:

example broken?

In the example, you have the following:
// Inherit from CD.Music.
CD.Music.Classical.prototype = CD.Music.prototype; // Inheritance.

// Add to the class and/or override as necessary.
CD.Music.Classical.prototype.composer = null;


Last I checked, JavaScript isn't going to copy CD.Music.prototype here, so you're going to be adding things to both classes, no? Shouldn't there be a "new" somewhere?

Theory wrote:

Re: example broken?

Bob

You are quite right. Fixed—thanks!

—Theory

Confluence: Enterprise Architecture wrote:

Javascript

Philosophy/PrincipalsJS Usage should be: it must be nongratuitous (i.e. provide real value, that could not be easily achieved another way) it must degrade well (i.e....

Kevin Bowker wrote:

Is there a way to construct a class so that the following code would be legitimate?

var disc=new CD();
disc.Music.Classical.composer='Mozart';

or

var disc=new CD();
var comp=disc.Music.Classical.composer;
comp = 'Mozart';

kevin@kbware.com

Ben Davies wrote:

You could put the inheritance code in the constructor, especially as you know the namespace chain anyway (your defining it at this point anyway!):

// CD.Music must be loaded already. Create the constructor.
CD.Music.Classical = function () {

    // Inherit from CD.Music.
    CD.Music.Classical.prototype =  CD.Music.prototype; // sets up the prototype inheritance chain
    CD.Music.Classical.prototype.constructor =  CD.Music; // constructor property is used in scripts to determine an object's type
    CD.Music.Classical.superclass =  CD.Music.prototype; // Sets up reference to superclass
    CD.Music.Classical.superclass.constructor.apply(this, arguments); // Call superclass constructor


    // Add to the class and/or override as necessary.
    this.composer  = null;
    this.orchestra = null;
    this.conductor = null;
    this.soloist   = null;
}

i.Skitz wrote:

JavaScript Namespace Module Exists

See http://ajile.sourceforge.net/ for a module that does JavaScript namespaces and more. The Wikipedia gives a good overview: http://en.wikipedia.org/wiki/Javascript_Namespaces.

Theory wrote:

Re: JavaScript Namespace Module Exists

Why would I want horrible Java-style namespces? See JSAN for my namespaces vision realized.

—Theory

cd music wrote:

// CD.Music must be loaded already. Create the constructor.
CD.Music.Classical = function () {

    // Inherit from CD.Music.
    CD.Music.Classical.prototype =  CD.Music.prototype; // sets up the prototype inheritance chain
    CD.Music.Classical.prototype.constructor =  CD.Music; // constructor property is used in scripts to determine an object's type
    CD.Music.Classical.superclass =  CD.Music.prototype; // Sets up reference to superclass
    CD.Music.Classical.superclass.constructor.apply(this, arguments); // Call superclass constructor


    // Add to the class and/or override as necessary.
    this.composer  = null;
    this.orchestra = null;
    this.conductor = null;
    this.soloist   = null;
}
Thanks

Theory wrote:

cd music,

The only problem with that apporoach to implementing the subclass is that you're redefining the inheritance hierarchy every time you instantiage a CD.Music.Classical object. Not very efficient.

—Theory

Tom Metro wrote:

The only downside to this proposal...we don’t get the block syntax...

You could probably sweeten the syntax with something like:

JSAN.subclass('CD::Music','CD::Music::Classical', {
  composer:  null;
  orchestra: null;
  conductor: null;
  soloist:   null;
});

Simply a JSAN variation on the instanceof(), inheritsfrom(), etc. helper functions that people have written.

Of course subclass() will have some challenges in sifting the newly supplied properties out of the passed object from properties inherited from Object.

-Tom