Weâll now explore some advanced patterns and utilities that I have found invaluable when working on larger applications, some of which have required a rethink of traditional approaches to application namespacing. Iâll note that I am not advocating any of the following as the way to namespace, but rather ways that I have found work in practice.
As weâve reviewed, nested namespaces can provide an
organized hierarchy of structure for a unit of code. An example of such
a namespace could be the following:
application.utilities.drawing.canvas.2d
. This
can also be expanded using the object literal pattern to be:
var
application
=
{
utilities
:
{
drawing
:
{
canvas
:
{
2
d
:
{
//...
}
}
}
}
};
One of the obvious challenges with this pattern is that each additional layer we wish to create requires yet another object to be defined as a child of some parent in our top-level namespace. This can become particularly laborious when multiple depths are required as our application increases in complexity.
How can this problem be better solved? In JavaScript Patterns, Stoyan Stefanov presents a very clever approach for automatically defining nested namespaces under an existing global variable. He suggests a convenience method that takes a single string argument for a nest, parses this, and automatically populates our base namespace with the objects required.
The method he suggests using is the following, which Iâve updated it to be a generic function for easier reuse with multiple namespaces:
// top-level namespace being assigned an object literal
var
myApp
=
myApp
||
{};
// a convenience function for parsing string namespaces and
// automatically generating nested namespaces
function
extend
(
ns
,
ns_string
)
{
var
parts
=
ns_string
.
split
(
"."
),
parent
=
ns
,
pl
;
pl
=
parts
.
length
;
for
(
var
i
=
0
;
i
<
pl
;
i
++
)
{
// create a property if it doesn't exist
if
(
typeof
parent
[
parts
[
i
]]
===
"undefined"
)
{
parent
[
parts
[
i
]]
=
{};
}
parent
=
parent
[
parts
[
i
]];
}
return
parent
;
}
// Usage:
// extend myApp with a deeply nested namespace
var
mod
=
extend
(
myApp
,
"myApp.modules.module2"
);
// the correct object with nested depths is output
console
.
log
(
mod
);
// minor test to check the instance of mod can also
// be used outside of the myApp namesapce as a clone
// that includes the extensions
// Outputs: true
console
.
log
(
mod
==
myApp
.
modules
.
module2
);
// further demonstration of easier nested namespace
// assignment using extend
extend
(
myApp
,
"moduleA.moduleB.moduleC.moduleD"
);
extend
(
myApp
,
"longer.version.looks.like.this"
);
console
.
log
(
myApp
);
Figure 13-1 shows the Chrome Developer Tools output:
Where one would previously have had to explicitly declare the various nests for their namespace as objects, this can now be easily achieved using a single, cleaner line of code.
Weâre now going to explore a minor augmentation to the Nested Namespacing pattern which weâll refer to as the Dependency Declaration pattern. We all know that local references to objects can decrease overall lookup times, but letâs apply this to namespacing to see how it might look in practice:
// common approach to accessing nested namespaces
myApp
.
utilities
.
math
.
fibonacci
(
25
);
myApp
.
utilities
.
math
.
sin
(
56
);
myApp
.
utilities
.
drawing
.
plot
(
98
,
50
,
60
);
// with local/cached references
var
utils
=
myApp
.
utilities
,
maths
=
utils
.
math
,
drawing
=
utils
.
drawing
;
// easier to access the namespace
maths
.
fibonacci
(
25
);
maths
.
sin
(
56
);
drawing
.
plot
(
98
,
50
,
60
);
// note that the above is particularly performant when
// compared to hundreds or thousands of calls to nested
// namespaces vs. a local reference to the namespace
Working with a local variable here is almost always faster than working
with a top-level global (e.g., myApp
). Itâs also both
more convenient and more performant than accessing nested
properties/subnamespaces on every subsequent line and can improve
readability in more complex applications.
Stoyan recommends declaring localized namespaces required by a function or module at the top of our function scope (using the single-variable pattern) and calls this a Dependency Declaration pattern. One of the benefits this offers is a decrease in locating dependencies and resolving them, should we have an extendable architecture that dynamically loads modules into our namespace when required.
In my opinion, this pattern works best when working at a modular level, localizing a namespace to be used by a group of methods. Localizing namespaces on a per-function level, especially where there is significant overlap between namespace dependencies, would be something I would recommend avoiding where possible. Instead, define it further up and just have them all access the same reference.
An alternative approach to automatic namespacing is deep object extension. Namespaces defined using object literal notation may be easily extended (or merged) with other objects (or namespaces) such that the properties and functions of both namespaces can be accessible under the same namespace post-merge.
This is something thatâs been made fairly easy to accomplish with modern JavaScript frameworks (e.g., see jQueryâs $.extend); however, if looking to extend objects (namespaces) using vanilla JS, the following routine may be of assistance.
// extend.js
// Written by Andrew Dupont, optimized by Addy Osmani
function
extend
(
destination
,
source
)
{
var
toString
=
Object
.
prototype
.
toString
,
objTest
=
toString
.
call
({});
for
(
var
property
in
source
)
{
if
(
source
[
property
]
&&
objTest
===
toString
.
call
(
source
[
property
])
)
{
destination
[
property
]
=
destination
[
property
]
||
{};
extend
(
destination
[
property
],
source
[
property
]);
}
else
{
destination
[
property
]
=
source
[
property
];
}
}
return
destination
;
};
console
.
group
(
"objExtend namespacing tests"
);
// define a top-level namespace for usage
var
myNS
=
myNS
||
{};
// 1. extend namespace with a "utils" object
extend
(
myNS
,
{
utils
:
{
}
});
console
.
log
(
"test 1"
,
myNS
);
// myNS.utils now exists
// 2. extend with multiple depths (namespace.hello.world.wave)
extend
(
myNS
,
{
hello
:
{
world
:
{
wave
:
{
test
:
function
(){
//...
}
}
}
}
});
// test direct assignment works as expected
myNS
.
hello
.
test1
=
"this is a test"
;
myNS
.
hello
.
world
.
test2
=
"this is another test"
;
console
.
log
(
"test 2"
,
myNS
);
// 3. what if myNS already contains the namespace being added
// (e.g. "library")? we want to ensure no namespaces are being
// overwritten during extension
myNS
.
library
=
{
foo
:
function
()
{}
};
extend
(
myNS
,
{
library
:
{
bar
:
function
(){
//...
}
}
});
// confirmed that extend is operating safely (as expected)
// myNS now also contains library.foo, library.bar
console
.
log
(
"test 3"
,
myNS
);
// 4. what if we wanted easier access to a specific namespace without having
// to type the whole namespace out each time?
var
shorterAccess1
=
myNS
.
hello
.
world
;
shorterAccess1
.
test3
=
"hello again"
;
console
.
log
(
"test 4"
,
myNS
);
//success, myApp.hello.world.test3 is now "hello again"
console
.
groupEnd
();
Note
The above implementation is not cross-browser compatible for all objects and should be
considered a proof-of-concept only. One may find the Underscore.js
extend()
method a simpler, more
cross-browser-friendly implementation to start with: http://documentcloud.github.com/underscore/docs/underscore.html#section-67.
Alternatively, a version of the jQuery $.extend()
method extracted from core can be found here: https://github.com/addyosmani/jquery.parts.
For developers who are going to use jQuery in their
applications, one can achieve the exact same object namespace
extensibility with $.extend
as
follows:
// top-level namespace
var
myApp
=
myApp
||
{};
// directly assign a nested namespace
myApp
.
library
=
{
foo
:
function
(){
//...
}
};
// deep extend/merge this namespace with another
// to make things interesting, let's say it's a namespace
// with the same name but with a different function
// signature: $.extend( deep, target, object1, object2 )
$
.
extend
(
true
,
myApp
,
{
library
:
{
bar
:
function
(){
//...
}
}
});
console
.
log
(
"test"
,
myApp
);
// myApp now contains both library.foo() and library.bar() methods
// nothing has been overwritten which is what we're hoping for.
For the sake of thoroughness, please see here for jQuery
$.extend
equivalents to the rest of the namespacing
experiments found in this section.
Reviewing the namespace patterns weâve explored in this section, the option that I would personally use for most larger applications is nested object namespacing with the Object Literal pattern. Where possible, I would implement this using automated nested namespacing, however, this is just a personal preference.
IIFEs and single global variables may work fine for applications in the small to medium range, however, larger code bases requiring both namespaces and deep subnamespaces require a succinct solution that promotes readability and scales. I feel this pattern achieves all of these objectives well.
I would also recommend trying out some of the suggested advanced utility methods for namespace extension, as they really can save time in the long run.
Get Learning JavaScript Design Patterns now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.