Problem
I have written a wrapper for my code that allows basic namespacing and I’d like some feedback on how it can be improved.
It is designed so that a module can define it’s namespace and get access to all of the parent namespaces. These are spread in reverse order across the arguments of the module (i.e. first.second.third
is passed as (third, second, first)
).
Here is the code itself:
(function(name, globals, factory) {
var namespaces = [];
name.split('.').reduce(function(object, key) {
if (typeof(object[key]) !== 'object') {
object[key] = {};
}
namespaces.push(object[key]);
return object[key];
}, globals);
factory.apply(null, namespaces.reverse());
})(/* namespace */, this, function(util, app) {
});
It is designed for use in the browser, hence this
being passed as the globals
argument (this
is window
in the browser).
Here’s an example of how I use it:
(function(name, globals, factory) {
var namespaces = [];
name.split('.').reduce(function(object, key) {
if (typeof(object[key]) !== 'object') {
object[key] = {};
}
namespaces.push(object[key]);
return object[key];
}, globals);
factory.apply(null, namespaces.reverse());
})('app.util', this, function(util, app) {
util.doSomething = function() {};
// I expose all "levels" of the namespace so that it can be used like this:
util.doSomethingElse = function() {
app.core.performAction();
};
});
One or two questions/thoughts I’ve had on it:
-
Is there a real reason I should use
this
instead ofwindow
? I’m not really sure why I started using it, I think I saw someone else using it and just picked it up. -
Is there any way to shorten it? I know it’s only ~400 bytes but it would still be nice to simplify it further. I could use
map
instead ofreduce
but I tried it and it only saved on one line and partially sacrificed readability and simplicity.
Solution
Is there a real reason I should use
this
instead ofwindow
?
window
is a browser-only global. this
is the current execution context. If your script runs in the global space, this
is window
. Either way will do.
Is there any way to shorten it?
Yes, by probably separating the code from the namespacer functionality.
(function(name, globals, factory) {
var namespaces = [];
name.split('.').reduce(function(object, key) {
if (typeof(object[key]) !== 'object') {
object[key] = {};
}
namespaces.push(object[key]);
return object[key];
}, globals);
factory.apply(null, namespaces.reverse());
})(/* namespace */, this, function(util, app) {
// Code here
});
If the code were to grow, you’d either be piling them up into that function, or rewriting the entire thing again in another file. Copy-pasting code is proably code smell. I suggest you split off the top part into a standalone function. The way I did this before is define it in a utilities global.
;(function(global, utils){
utils.namespace = function(namespace, closure){
closure.call(null, namespace.split('.').reduce(function(parent, current, index, keys){
if(!parent.hasOwnProperty(current)) parent[current] = {};
return parent.current;
}, global));
};
}(this, this.utils = this.utils || {});
// usage
utils.namespace('foo.bar.baz', function(baz){
// baz is `window.foo.bar.baz`
});
utils.namespace('path.to.app', function(app){
// app is `window.path.to.app`
});
So I managed to make a few improvements using map
instead of reduce
, but I’m sure there’s still something I can improve.
(function(name, globals, factory) {
var namespaces = name.split('.').map(function(key) {
globals[key] = typeof(globals[key]) === 'object' ? globals[key] : {};
globals = globals[key];
return globals;
});
factory.apply(null, namespaces.reverse());
})(/* namespace */, this, function(/* namespace pieces */) {
});
Changes:
-
Replaced
reduce
withmap
so I don’t need an array to store the namespace values in. -
Now uses a ternary conditional instead of a full conditional to replace a value if it is not an object.