15

My javascript file is getting pretty big (3000+ lines) and I'm getting confused as to how to layout my file and delare functions so that they can called anywhere in the file.

To summarise my JS file looks a little like this at the moment:

//ALL GLOBAL VARIABLES FIRST DECLARED HERE
var var1 , var2 ,var3

$(document).ready(function(){

//JQUERY STUFF

});

//ALL FUNCTIONS THAT NEED TO BE GLOBAL DECLARED HERE
function myFunction(){
//do some stuff here
}

I am running into problems with this as some functions I call in places don't seem to be declared at the time of calling or aren't available globaly. It's all very confusing now!

Could someone suggest the best way to layout a big js/jquery file with certain JS Functions, Objects and Variables available to be referenced anywhere in the file.

UPDATE:

So to simplify it this correct (see my comments)?

window.MainModule = (function($, win, doc, undefined) {//WHAT IS BEING PASSED IN HERE?
    var foo, bar, modules; //VARIABLES ACCESSIBLE ANYWHERE

    var modules["foobar"] = (function() {//WHAT IS A MODULE? WHEN WOULD I USE A SEPERATE MODULE?
        var someFunction = function() { ... };//DECLARING MY FUNCTIONS?

        ...

        return { 
            init: someFunction,//IS THIS WHERE I USE/BIND MY FUNCTIONS TO EVENTS AND ELEMENTS?
            ... 
        };
    }());

    // hoist a variable into global scope
    window.Global = someLocal;

    return { 
        init: function() {//FUNCTION TO INIT ALL MODULES?
            for (var key in modules) {
                modules[key].init();
            }
        }
    };
}(jQuery, this, document));
wilsonpage
  • 17,341
  • 23
  • 103
  • 147
  • 3
    On a side note, I personally split up my modules into separate files to keep it all organised. – pimvdb Feb 25 '11 at 16:44
  • In the layout that you have all functions in the 'global' section will be available globally. The file has to have finished downloading before it will 'execute'. The problem you might have is if you were trying to access a global function from a separate script file. – Blair McMillan Feb 25 '11 at 16:45
  • 2
    I think splitting up the file in logical parts is the best way. You can always combine it again with build tools. I think even a 500 lines file is too big.... 3000 lines would drive me insane. – Felix Kling Feb 25 '11 at 16:48
  • @Blair: Scripts from external files are downloaded and executed in the order they're embedded, unless you use the `defer` attribute. – Marcel Korpel Feb 25 '11 at 16:53
  • @Felix Kling anything under 1500 is manageable as long as it's split into sub modules. @MarcelKorpel `defer` isn't stable cross-browser. – Raynos Feb 25 '11 at 17:01
  • @Marcel I know. That's exactly what I was saying. If he has everything in a single file then it will all be available. If he has his global functions in file2 but is trying to access them from file1 then they may not be available (depending on size, download time etc). – Blair McMillan Feb 25 '11 at 17:01
  • @Blair: But that has generally nothing to do with size or download time: they *may* be downloaded simultaneously, but they *will* be executed in the order they were included, one after the other, as JavaScript is single-threaded. If file1 is included in the markup before file2, you can safely access functions defined in former from within the latter. – Marcel Korpel Feb 25 '11 at 21:57
  • @Marcel - Really? Learnt something new today. Thanks. – Blair McMillan Feb 28 '11 at 09:08

3 Answers3

7

The modules section isn't properly defined ... here's a slightly tidied up example.

window.MainModule = (function($, win, doc, undefined) {
    var modules = {};

    // -- Create as many modules as you need ...
    modules["alerter"] = (function(){
        var someFunction = function(){ alert('I alert first'); };

        return { 
            init: someFunction
        };
    }());

    modules["alerter2"] = (function(){
        var someFunction = function(){ alert('I alert second'); };

        return { 
            init: someFunction
        };
    }());

    return { 
        init: function(){
            for (var key in modules){
                modules[key].init();
            }
        }
    };
}(jQuery, this, document));

$(window.MainModule.init);
Jeff Parker
  • 7,367
  • 1
  • 22
  • 25
  • What is the best way to call a function declared in one module from another within the above context? I think you answered this already but I don't have record of the code :( – wilsonpage Feb 26 '11 at 10:30
5
// We always use closures don't we?
window.MainModule = (function($, win, doc, undefined) {
    var foo, bar, modules; // List of local variables. 

    var modules["foobar"] = (function() {
        var someFunction = function() { ... };

        ...

        return { 
            init: someFunction,
            ... 
        };
    }());

    // hoist a variable into global scope
    window.Global = someLocal;

    return { 
        init: function() {
            for (var key in modules) {
                modules[key].init();
            }
        }
    };
}(jQuery, this, document));

// Let's kick off the MainModule on $.ready
// I recommend you do this in your `html` with page specific data.
$(window.MainModule.init);

[[Disclaimer]]: This is a pseudo-code module with some standard code excluded for brevity.

Anything declared with var x inside your main closure is available throughout the entire function. Of course it won't be set to what you expect it to be set unless you set it.

To control loading and flow split code into what's automatically executed in your self executing closure and what needs to manually inited by your controller with page/user specific parameters.

Raynos
  • 166,823
  • 56
  • 351
  • 396
  • Ok so where does the jquery 'doc ready' fit into that and how do I make certain functions available throughout the file? – wilsonpage Feb 25 '11 at 16:48
  • 1
    As an improvement to your module closure, you can pass in `this` to your self-executing function to ensure `window` cannot be hijacked by another module: `((function (window) { ... })(this); – Ates Goral Feb 25 '11 at 16:49
  • Exposing a function (or variable) in the global scope is not needed when everything is inside the anonymous, self-invoking function (it's *not* a closure in strict sense) and even breaks the benefit of this module pattern. – Marcel Korpel Feb 25 '11 at 16:49
  • @Ates: Of course, that's why people always pass (global) `this` instead `window`, thanks! – Marcel Korpel Feb 25 '11 at 16:50
  • @MarcelKorpel hoisting some data into global scope is important. Libraries use this to make for example `jQuery` global. – Raynos Feb 25 '11 at 16:51
  • @pagewil Go look at [JSGarden](http://bonsaiden.github.com/JavaScript-Garden/#scopes). Every `var` statement get's hoisted upto the nearest function scope. – Raynos Feb 25 '11 at 16:53
  • @Raynos: So what you'e saying is that everything should be inside that MainModule function. I have so many questions because I have never properly learnt how to layout my JS Script. Where do the bulk of my functions go and where do the bulk of my eventhandler assignments go. Could you explain your suggestion more clearly? – wilsonpage Feb 25 '11 at 16:56
  • @pagewil look at [Macro layout](http://stackoverflow.com/questions/5095525/approach-to-handle-javascript-on-bigger-projects) and [Module layout](http://stackoverflow.com/questions/5083409/pattern-for-javascript-module-pattern-and-sub-module-initialization). Feel free to ask more specific questions in [Chat](http://chat.stackoverflow.com/rooms/17/javascript). My personal style is to have all event binding in `init` functions and then have the event handler in various modules, the functions in modules. You only need one global function. `Module.init(data1, data2, ...)` – Raynos Feb 25 '11 at 16:58
  • @Raynos can you see my UPDATE and help explain this a little further. I think I am beginning to understand but am just so unfamiliar to this kind of javascript initiation. – wilsonpage Feb 25 '11 at 17:13
  • 1
    @pagewil This is just a particular pattern. There are multiple different ways of structuring code. MVC patterns are great. Have a read of this [article](http://peter.michaux.ca/articles/mvc-architecture-for-javascript-applications) It shows some nice code examples. Also take a [look](http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth) – Raynos Feb 25 '11 at 17:52
  • @Raynos What is the best way to call a function declared in one module from another? – wilsonpage Feb 26 '11 at 10:30
4

You can either declare them in Window scope:

window.variableName = myVariable;

or you can omit the var, which is the same as declaring something in window scope:

variableName = myVariable;
jjrdk
  • 1,882
  • 15
  • 18
  • 1
    The last remark is not entirely true: the JS engine goes up in the scope and if it finds a variable with that name in another scope, *that one* is changed, instead of a global variable being created. – Marcel Korpel Feb 25 '11 at 16:52
  • 1
    not to mention that someone will come along and go "Hmm he forgot a `var`" and _fixes_ your code, or in this case breaks it. – Raynos Feb 25 '11 at 16:54
  • @Marcel Korpel - That's a good point. @Raynos - I see your point, but I wasn't advocating that solution, I was showing it as an option. – jjrdk Feb 25 '11 at 19:01