1
\$\begingroup\$

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:

  1. Is there a real reason I should use this instead of window? I'm not really sure why I started using it, I think I saw someone else using it and just picked it up.

  2. 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 of reduce but I tried it and it only saved on one line and partially sacrificed readability and simplicity.

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

Is there a real reason I should use this instead of window?

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`
});
\$\endgroup\$
2
  • 2
    \$\begingroup\$ Just out of curiosity, why did you place a semicolon right before the anonymous function in your last snippet? I've seen that done before, but wasn't 100% sure why (I thought it was something to do with ending the "last line", but I'm not sure why'd there by a "last line"). \$\endgroup\$
    – SirPython
    Commented May 23, 2016 at 2:15
  • 2
    \$\begingroup\$ @SirPython It's a habit :D It's a practice to guard an IIFE from naive concatenation of files. Prevents the parens from being interpreted as something else, like a function call (i.e.: functionNameFromPreviousFile;(function iife(){}()) instead of functionNameFromPreviousFile(function iife(){}())) \$\endgroup\$
    – Joseph
    Commented May 23, 2016 at 2:42
0
\$\begingroup\$

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:

  1. Replaced reduce with map so I don't need an array to store the namespace values in.

  2. Now uses a ternary conditional instead of a full conditional to replace a value if it is not an object.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.