Eli Grey

Namespacing properties in JavaScript

Namespacing properties is a great way to make a JavaScript library produce extendible objects. Namespacing in JavaScript can be done by prefixing namespace:: before object and property names. Unfortunately, namespacing is only supported in JavaScript 1.6 and higher, which is currently only implemented in SpiderMonkey (Firefox’s JavaScript engine) and Rhino with E4X enabled. Namespaces should have a toString method that returns a string of the same name (Foo.toString() == "Foo"). In Firefox, the @mozilla.org/js/function namespace (“function”) seems to be the default at which everything is under. For example, function::document == this.document and x = "@mozilla.org/js/function"; x::document == function::document. Also, anything with a blank string representation behaves the same way as long as it’s being used in a property, for example:

var foo = "@mozilla.org/js/function", bar = "";
try { // using try..catch because the error still fires in a typeof statement
    foo::document; // works fine
    bar::document; // error
} catch(err if err instanceof ReferenceError) {
    // err.message == "reference to undefined XML name document";
}
this.bar::document == this.document;

The following example shows a naming conflict that can be solved using namespaced properties. In the examples, the fictional libraries, libSpaceTime and libDimensions are loaded. libDimensions adds a Size global and creates a ‘dimensions’ setter on Object.prototype that accepts various inputs to change the width/height/depth/ect. of something. libSpaceTime introduces the SpaceTime global. When a universe is created, the dimensions setter changes the number of dimensions the universe has. The width/height/ect. of a universe are automatically set (but can be changed) from the matter property.

var foo = new SpaceTime.createUniverse({
    dimensions: 4,
    energy: 1.7976931348623158e+308,
    matter: 130
});
 
foo.dimensions = 5;
Object.getOwnPropertyDescriptor(Object.prototype, "dimensions").set.call(foo, {width: 1});

In the example, it was impossible to use libDimensions’ dimensions setter to change foo without using Object.getOwnPropertyDescriptor(Object.prototype, "dimensions").set. This is where namespacing properties can help. In the following example, all of the extra properties are namespaced to their global constructors, making property name collisions very unlikely. Assuming that the same original foo universe exists, the code would now be:

foo.SpaceTime::dimensions = 5;
foo.Size::dimensions = {width: 1};

Another useful thing that works in conjunction with namespacing properties is using @-properties. For example, instead of using all the necessary methods to create a star, I could use the experimental createStar method that hasn’t been standardized yet, which would be accessible via foo.@createStar() if the library doesn’t namespace properties or foo.@SpaceTime::createStar() if the library does. This is easier to type than foo.__createStar__() and foo.SpaceTime::__createStar__(). About anywhere you start a property name with _, @ could be used instead. Of course, if your library is using E4X + XML, you should never do this due to @-properties being for XML tag attributes.

If you would like to use namespaced properties in browsers that don’t support JavaScript 1.6 or higher, you could use a format like object[n(namespace, property [, useAtSign])] where n is a function that returns the namespaced property name. I have created a simple reference function for this:

function n/*amespace*/(ns, prop, at) {
    return (at ? "@" : "") + ns + "::" + prop;
}

One Comment (add yours)

Leave a Reply