The module pattern is a common JavaScript coding pattern. It's generally well understood, but there is a number of advanced uses that has not gotten a lot of attention. In this article, I'll review the basics and cover some truly remarkable advanced topics, including one which I think is or Iginal.
The Basics
We ' ll start out with a simple overview of the module pattern, which have been well-known since Eric Miraglia (of YUI) first Blogged about it three years ago. If you ' re already familiar with the module pattern, feel-to-skip ahead to "Advanced Patterns".
Anonymous Closures
This are the fundamental construct that makes it all possible, and really are the single best feature of JavaScript . We ll simply create an anonymous function, and execute it immediately. All of the code that runs inside the function lives in a closure, which provides privacy and state throughout the lifetime of our application.
(function () {// ... all vars and functions are in this scope only// still maintains access to all globals}());
Notice The ()
around the anonymous function. This was required by the language, since statements that begin with the token was always function
considered to be function Declarations. Including ()
creates a function expression instead.
Global Import
JavaScript has a feature known as implied globals. Whenever a name is used, the interpreter walks the scope chain backwards looking for a var
statement for that name. If none is found, which variable is assumed to be global. If it's used in a assignment, the global is created if it doesn ' t already exist. This means, that using, or creating global variables in an anonymous closure are easy. Unfortunately, this leads to Hard-to-manage code, as it's not obvious (to humans) which variables is global in a given fi Le.
Luckily, our anonymous function provides a easy alternative. By passing globals as parameters-anonymous function, we import them into our code, which is both clear Er and faster than implied globals. Here's an example:
(function ($, YAHOO) {// now have access to globals jQuery (as $) and YAHOO in this code}(jQuery, YAHOO));
Module Export
Sometimes you don ' t just want to use globals, but you want to declare them. We can easily do the exporting them, using the anonymous function ' s return value. Doing so would complete the basic module pattern and so here's a complete example:
var MODULE = (function () {var my = {},privateVariable = 1;function privateMethod() {// ...}my.moduleProperty = 1;my.moduleMethod = function () {// ...};return my;}());
Notice that we ' ve declared a global module named MODULE
, with the public Properties:a method named and MODULE.moduleMethod
a variable nam Ed MODULE.moduleProperty
. In addition, it maintains private internalstate using the closure of the anonymous function. Also, we can easily import needed globals, using the pattern we learned above.
Advanced Patterns
While the above are enough for many uses, we can take this pattern farther and create some very powerful, extensible constr Ucts. Lets work through them one-by-one, continuing with our module named MODULE
.
Augmentation
One limitation of the module pattern so far is, the entire module must be in one file. Anyone who had worked in a large code-base understands the value of splitting among multiple files. Luckily, we have a nice solution to augment modules. First, we import the module, then we add properties and then we export it. Here's an example, augmenting we from MODULE
above:
var MODULE = (function (my) {my.anotherMethod = function () {// added method...};return my;}(MODULE));
We use var
the keyword again for consistency, even though it's not necessary. After the this code had run, our module would have gained a new public method named MODULE.anotherMethod
. This augmentation file would also maintain its own private internal state and imports.
Loose Augmentation
While our example above requires we initial module creation to is first, and the augmentation to happen second, that isn ' t always necessary. One of the best things a JavaScript application can does for performance are to load scripts asynchronously. We can create flexible multi-part modules that can load themselves in any order with loose augmentation. Each file should has the following structure:
var MODULE = (function (my) {// add capabilities...return my;}(MODULE || {}));
In this pattern, the var
statement are always necessary. Note that the import would create the module if it does not already exist. This means can use a tool like LABJS and load all of the your module files in the parallel, without needing to block.
Tight Augmentation
While loose augmentation are great, it does place some limitations on your module. Most importantly, you cannot override module properties safely. You also cannot the use module properties from the other files during initialization (if you can at Run-time after intialization) . tight augmentation implies a set loading order, but allows overrides. Here are a simple example (augmenting our original MODULE
):
var MODULE = (function (my) {var old_moduleMethod = my.moduleMethod;my.moduleMethod = function () {// method override, has access to old through old_moduleMethod...};return my;}(MODULE));
Here we've overridden MODULE.moduleMethod
, but maintain a reference to the original method, if needed.
Cloning and inheritance
var MODULE_TWO = (function (old) {var my = {},key;for (key in old) {if (old.hasOwnProperty(key)) {my[key] = old[key];}}var super_moduleMethod = old.moduleMethod;my.moduleMethod = function () {// override method on the clone, access to super through super_moduleMethod};return my;}(MODULE));
This pattern is perhaps the least flexible option. It does allow some neat compositions, and that's comes at the expense of flexibility. As I ' ve written it, properties which is objects or functions would not be duplicated, they would exist as one obje CT with references. Changing one would change the other. This could is fixed for objects with a recursive cloning process, but probably cannot is fixed for functions, except PERHA PS with eval
. Nevertheless, I ' ve included it for completeness.
Cross-file Private State
One severe limitation of splitting a module across multiple files is this each file maintains their own private state, and D OES not get access to the private state of the other files. This can is fixed. Here's an example of a loosely augmented module, that'llmaintain private state across all augmentations:
var MODULE = (function (my) {var _private = my._private = my._private || {},_seal = my._seal = my._seal || function () {delete my._private;delete my._seal;delete my._unseal;},_unseal = my._unseal = my._unseal || function () {my._private = _private;my._seal = _seal;my._unseal = _unseal;};// permanent access to _private, _seal, and _unsealreturn my;}(MODULE || {}));
Any file can set properties on their local variable _private
, and it'll be immediately available to the others. Once This module have loaded completely, the application should call MODULE._seal()
, which would prevent external access to the Inter NAL _private
. If This module were to is augmented again, further in the application ' s lifetime, one of the internal methods, in any file , can call _unseal()
before loading the new file, and call _seal()
again after it has been executed. This is the pattern occurred to me today while I am at work, I has not seen this elsewhere. I think this is a very useful pattern, and would has been worth writing about all on its own.
Sub-modules
Our final advanced pattern is actually the simplest. There is many good cases for creating sub-modules. It's just like creating regular modules:
MODULE.sub = (function () {var my = {};// ...return my;}());
While this could have been obvious, I thought it worth including. Sub-modules has all the advanced capabilities of normal modules, including augmentation and private state.
Conclusions
Most of the advanced patterns can is combined with each other to create more useful patterns. If I had to advocate a route to take in designing a complex application, I ' d combine loose augmentation, priv Atestate, and sub-modules.
I haven ' t touched on performance here @ all, but I ' d like to put in one quick note:the module pattern are good for PE Rformance. It Minifies really well, which makes downloading the code faster. Using Loose augmentation allows easy non-blocking parallel downloads, which also speeds up download speeds. Initialization time is probably a bit slower than other methods, but worth the trade-off. Run-time performance should suffer no penalties so long as globals is imported correctly, and would probably gain speed in Sub-modules by shortening the reference chain with local variables.
To close, here's a example of a sub-module that loads itself dynamically to its parent (creating it if it does not exist) . I ' ve left the private state for brevity, but including it would is simple. This code pattern allows a entire complex heirarchical code-base to being loaded completely in parallel with itself, sub-mod Ules and all.
var UTIL = (function (parent, $) {var my = parent.ajax = parent.ajax || {};my.get = function (url, params, callback) {// ok, so I‘m cheating a bit :)return $.getJSON(url, params, callback);};// etc...return parent;}(UTIL || {}, jQuery));
I hope this have been useful, and please leave a comment to share your thoughts. Now, go forth and write better, more modular javascript!
This post was featured on Ajaxian.com, and there are a little bit more discussion going on there as well, which is worth re Ading In addition to the comments below.
Http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html
http://javascriptweblog.wordpress.com/2010/12/07/namespacing-in-javascript/
JavaScript Module pattern:in-depth