Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I'm in the process of building a website for the company I work for and I'm trying to think of the best way to namespace and organize the site's JavaScript code.

I've written several jQuery/JavaScript plugins for the big chunks of code, but now I'd like a namespaced place to "call" said plugins and any other random JavaScript that's needed throughout the site.

I'm considering using a singleton (NAMESPACE.js):

// http://stackoverflow.com/a/1479341/922323
var NAMESPACE = (function() {

    //var privateVar = '';

    function weather() {

        var $weather = $('#weather');

        if ($weather.length) {

            // Do jQuery stuff with $weather here.

        }

    } // weather()

    function navigation() {

        var $baz = $('#baz'),
        $html = $('html');

        $baz
            .myOtherPlugin({
                cloneRemove : 'li > div',
                cloneId     : false
            })
            .anotherPlugin({
                eventType      : 'hoverIntent',
                onStartOutside : function() {
                    $html.addClass('mw-outside');
                },
                onEndOutside   : function() {
                    $html.removeClass('mw-outside');
                }
            });

    } // navigation()

    function footer() {

        $('footer ul').myPlugin({
            //openTab : true,
            currentPage : true
        });

    } // footer()

    return {

        init : function() {

            weather();

            navigation();

            footer();

            // Add more here as project grows...

        } // init()

    };

})();

HTML would look like so (in foot of document):

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="plugins.js"></script>
<script src="NAMESPACE.js"></script>

<script type="text/javascript">
    <!--

        $(document).ready(function() {

            NAMESPACE.init();

        });

    //-->
</script>

Question(s):

  1. Do you see anything out of the ordianary with my (pseudo) code above?
  2. Am I headed in the right direction? If not, could you suggest an alternative?
  3. Am I over-complicating things?
  4. I really like how Yahoo! does: YAHOO.namespace("myProject"); YAHOO.myProject.myModule = function () { ... } ... it seems like if I did something similar, that would allow me to easily add/remove "modules" on a "per page" basis. Would anyone recommend this approach? If so, how could I modify my example to mimic YUI's functionality (I'm going to research YUI's namespace() code and post back my results if I figure it out).
share|improve this question

1 Answer 1

up vote 5 down vote accepted

One problem in your code is that the "modules" aren't standalone. You'd be adding the "modules" for calling into the init every time a new on is made.

To avoid doing so, each module should register itself for init. You can do this by adding a "register" functionality in your singleton.

(function(exports){

    var registry = []; //collection of module

    //adds module to collection
    exports.register = function(moduleDeclaration){
        registry.push(moduleDeclaration);
    }

    //executes every module
    exports.init = function(){
        var registryLength,i;

        registryLength = registry.length

        //loop through each module and execute
        for(i=0;i<registry.length;i++){
            registry[i].call(this);
        }
    }

//use existing namespace or make a new object of that namespace
}(window.Lib = window.Lib || {}));

//To register a module
Lib.register(function(){
    //do what you have to do here
});

//executing the init
Lib.init();

See? The modules register themselves and don't need to be added manually for init. A developer can create a module on a separate file, add it into the page without editing the main Lib singleton.

Also, you'd have to consider what you'd do if you include a module dynamically after init is called. You should have some internal flag to indicate that init was already called, and that dynamically added module should register and execute immediately.


A vaguely known library called KernelJS already does this and contains many more features like core abstraction, pub-sub inter-module communication, register-remove modules, loose coupling pattern and so on. I suggest you try it or base from it.


As an answer to your add-on question of exposing modules, sure you can. However, according to scalability principles, modules or "widgets" as they call them, should not know each exists. This is to prevent tight-coupling and avoid breakage when, for example, you take out a certain module out.

Also, naming modules isn't good for portability as you are setting a fixed name for a set of code. What if you changed it's name? You'd have to look for all code that depends on that module and change the name.

However, if you want to have named modules and fetching badly, you could expose another function to retrieve a module and return that module if it exists, or undefined or a custom error if it doesn't. Here's modified code for Lib. Since we are using named modules, instead of an array, registry is now an object.

var registry = {}

exports.register = function(name,declaration){
    registry[name] = declaration;
}

exports.init = function(){
    var module,i;

    registryLength = registry.length

    //loop through each module and execute
    for(name in registry){
        if(registry.hasOwnProperty(name)){
            registry[name].call(this);
        }
    }
}

exports.getModule = function(moduleName){

    //default to undefined
    var module;

    //if module exists, assign
    if(registry.hasOwnProperty('moduleName')){
        module = registry[moduleName];
    }

    return module
}

//to register a module
Lib.register('moduleName',function(){
    //module code
});

Additionally, you can call each module's init from Lib's init. Just make sure each module exposes an init function.

share|improve this answer
    
Joseph... YOU ROCK!!! Thank you so much for the very helpful and informative reply! I'm playing with your example now (KernelJS looks awesome also... I'll play with that second). I'll be back shortly with my findings. :) –  mhulse Aug 29 '12 at 17:42
    
Based off of your AWESOME code, I've got a working example here. I've updated my question with the code (for easy access/viewing). I love it! My only question: Would there ever be a situation where I'd want to access modules after init()? I played around with exposing the registry, but I'd have to know the key of the module I want to access... Would it be worth it to add some sort of named key to the registry and publicly expose it for later access? Does any of that make sense? :D –  mhulse Aug 29 '12 at 23:04
    
@MickyHulse updated the answer. –  Joseph the Dreamer Aug 30 '12 at 3:24
    
Joseph... You, sir, are a genius!!!! Thanks a billion for all of your pro help! I greatly appreciate it! :) –  mhulse Aug 30 '12 at 17:07
    
I do have one last question, does my if (initialized) moduleDeclaration.call(this); line meet your standards? Is that how/where you would handle registering and executing a module immediately? –  mhulse Aug 30 '12 at 17:10

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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