Namespaces and Globals

Henrik Vendelbo edited this page May 21, 2013 · 7 revisions

When your page needs more than a couple of common variables and functions, you need a way to divide them into packages. In JavaScript that is easy. Just create a few objects and divide your code between them.

var shapes = {};
shapes.Shape = function() {};

var tools = {};
tools.Tool = function() {};

This works well for websites with a moderate amount of code. Eventually you will want a simple way to specify a hierachialy named package supported natively by most modern languages. This classic functionality packaging can be done easily with a window resolver.

var shapes = Resolver("window")("my.shapes");
var tools = Resolver("window")("my.tools");

If you call Resolver() without a parameter, the default behaviour is the look up the package within the default namespace object. This is called the default namespace.

Instead of defining your application on the window object you should use the default namespace which avoids the performance overhead of the global lookup.

You can access shapes and tools in the default namespace using short form,

var shapes = Resolver("::my.shapes::");
var tools = Resolver("::my.tools::");

Undefined parts are stubbed out with plain objects. This is usually what you want to do for the package, but not for the functionality within the package. You can assign an entry in a package using a single statement.

Resolver().set("my.shapes.IGNORE_X",5);

If you want to declare a value without overwriting a previous one set elsewhere or configured, call declare.

Resolver().declare("my.shapes.IGNORE_X",5);

When Undefined

When you try to resolve "my.shapes.IGNORE_X" before it has been defined the default behavior is to fill in the blanks with generated objects. To get null or have an error thrown pass the undefined strategy as the second parameter.

var shapes = Resolver()("my.shapes","null");
var shapes = Resolver("::my.shapes::","null");

If either my or my.shapes is undefined null will be returned.

var shapes = Resolver()("my.shapes","throw");
var shapes = Resolver("::my.shapes::","throw");

If either my or my.shapes is undefined an instance of Error will be thrown.

N.B.: The default for get might be changed to "null", and "generate" will be an option.

Private Namespaces

If you bundle your code in one big file and use a JavaScript Scope to encapsulate it, you can keep out of the global namespace altogether by using a private namespace.

var Private = Resolver({});

will create a resolver for a private namespace using the blank object passed as a parameter. Use it to declare and resolve functionality.

Private.declare("my.shapes.Rectangle",Generator(Rectangle));
var ff = Private("my.shapes.Rectangle")(5,5);

In its basic form this approach doesn't do what you can do with local variables, but it can be used in a more advanced scenario to make a namespace and pass it among scoped scripts or even browser windows.

Alternate Namespaces

You could also bundle your code and use a named namespace, keeping out of the global namespace, but still allowing other scripts to resolve the contents of the namespace.

var visualns = {};
var Visual = Resolver("visual",visualns);

will create a resolver for a namespace named visual. visualns will now point to your own alternate namespace. Calls to Visual(...) will create objects attached to visualns namespace rather than the default namespace. Note that calling Resolver("visual",other_ns) will not change the current namespace as it is already set. To override the existing one call Resolver("visual").override(other_ns).

Calling Resolver("visual") somewhere else will also resolve objects in the visualns namespace.

Visual.declare("my.shapes.Rectangle",Generator(Rectangle));
var ff = Visual("my.shapes.Rectangle")(5,5);

Declaration and resolution can be done exactly alike for any other namespace.

The above style of writing is usually what you want as it limits the global object access. If you need one shot expressions without creating a resolver function, you should use the shortcut form,

Resolver("visual::my.shapes.Rectangle","declare",Generator(Rectangle));
var myRect = Resolver("visual::my.shapes.Rectangle::")(5,5);

Changing the default namespace

By default the global namespace is resolved. You can change this by,

var my_default = {};
Resolver("default",my_default);

This will cause all subsequent Resolver() calls to resolve within my_default.

Resolver("default").override(window);

This will cause all subsequent Resolver() calls to resolve within the global window object.

Custom Packages

New packages in a namespace is by default created by generating a plain object. You can supply your own generator as an option to Resolver. The default behaviour is equivalent to,

var my_resolver = Resolver("island", {}, { "generator":Generator() })

A call like my_resolver("a.b.c") will use the default generator to construct three nested objects.

function Package(enclosing,name,resolver) { this.__enclosing__ = enclosing; this.__name__ = name; }
var my_resolver = Resolver("island", {}, { "generator":Generator(Package) });
my_resolver("my.shapes.advanced");

will construct each of the packages my, shapes, and advanced as instanceds of Package, with the package name and a reference to the enclosing package.

Pre-resolved references

It is sometimes useful to get a reference to a lookup.

private_resolver.reference(full name)

returns an object with get, set, and declare.

var uiDialog = Resolver().reference("ui.Dialog");
function Dialog(){
    
}
uiDialog.set(Dialog);

In sequence this might not be so useful, but you could return uiDialog from a function and make it configurable.

A common variant can be used for declaring content of packages.

var ui = Resolver().resolver("ui");
function Dialog() {
}
ui.set("Dialog",Generator(Dialog));
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.