🍦 JavaScript Pattern - Module
Updated at 2013-05-30 14:15
Module pattern is the most basic way of structuring JavaScript code. You can use whole pattern in with pure JavaScript and parts of it with all frameworks e.g. Backbone and jQuery. You can think of them as classes of JavaScript.
// If the module does not need to be accessed from the outside,
// do not save to a variable.
// Adding imported modules in function call makes them faster and
// helps in compression e.g. `window` can be named `w`.
var PublicModule = (function(window, $) {
'use strict';
/**
* Constructor.
*/
var PublicModule = function PublicModule(options) {
options = options || {};
// Public variables can be accessed and changed straight.
this.valueSetOnInit = options.valueSetOnInit;
// Private variables should be prefixed with an underscore to
// mean that they are not to be accessed straight or modified.
this._accessorVar = 'DEFAULTPUB'
return this;
};
/**
* Public functions.
*/
PublicModule.prototype.genericFunction = function() {
return this.valueSetOnInit;
};
PublicModule.prototype.setAccessorVar = function(value) {
this._accessorVar = value;
};
PublicModule.prototype.getAccessorVar = function() {
return this._accessorVar;
};
/**
* Static variables.
* Same variable is accessed by all PublicModule instances,
* followed by few public functions that access it.
*/
var staticVar = 'STATIC';
PublicModule.prototype.setStaticVar = function(value) {
staticVar = value;
};
PublicModule.prototype.getStaticVar = function() {
return staticVar;
};
return PublicModule;
}(window, window.jQuery));
/**
* Usage Examples.
*/
var one = new PublicModule({
valueSetOnInit: 'THIS IS ONE'
});
var two = new PublicModule({
valueSetOnInit: 'THIS IS TWO'
});
var three = new PublicModule({
valueSetOnInit: 'THIS IS THREE'
});
console.assert( one.valueSetOnInit === 'THIS IS ONE');
console.assert( two.valueSetOnInit === 'THIS IS TWO');
console.assert( three.valueSetOnInit === 'THIS IS THREE');
// Public variables are accessible and individual.
one.setAccessorVar('ONEPUB');
two.setAccessorVar('TWOPUB');
console.assert( one.getAccessorVar() === 'ONEPUB');
console.assert( two.getAccessorVar() === 'TWOPUB');
console.assert( three.getAccessorVar() === 'DEFAULTPUB');
// Variables that should not be accessed straight should be
// prefixed with underscore. It does not forbid from accessing them
// but atleast people understand they might not get expected results.
console.assert( one._accessorVar === 'ONEPUB');
console.assert( two._accessorVar === 'TWOPUB');
console.assert( three._accessorVar === 'DEFAULTPUB');
// Static variables are same for all instances.
console.assert( one.getStaticVar() === 'STATIC');
console.assert( two.getStaticVar() === 'STATIC');
console.assert( three.getStaticVar() === 'STATIC');
two.setStaticVar('NEW STATIC!');
console.assert( one.getStaticVar() === 'NEW STATIC!');
console.assert( two.getStaticVar() === 'NEW STATIC!');
console.assert( three.getStaticVar() === 'NEW STATIC!');
// Calling some public functions.
console.assert( one.genericFunction() === 'THIS IS ONE');
console.assert( two.genericFunction() === 'THIS IS TWO');
console.assert( three.genericFunction() === 'THIS IS THREE');
You can augment module with additional functionality. This lets you split big modules into smaller files if required, even on the fly. This even extends all already created instances.
var PublicModule = (function($, PublicModule) {
'use strict';
PublicModule.prototype.augmentedFunction = function() {
return this.valueSetOnInit;
};
return PublicModule;
}(window.jQuery, PublicModule));
// You can also make the base module optional:
// }(window.jQuery, PublicModule || function() {}));
// `one` was created before augmentation was even made
// and still you can now use this.
console.assert( one.augmentedFunction() === 'THIS IS ONE');
You can extend modules much like object-oriented inheritance.
var ExtendedModule = (function($, Parent) {
'use strict';
// First we use the default constructor.
var ExtendedModule = function ExtendedModule(options) {
this.extendedVar = options.extendedVar;
Parent.prototype.constructor.call(this, options);
}
// Next we copy all the functions the parent has.
// Same as Object.create() but that is not supported in IE8.
ExtendedModule.prototype = (function(proto) {
var ExtendedModule = function (){}
ExtendedModule.prototype = proto;
return new ExtendedModule;
}(Parent.prototype));
ExtendedModule.prototype.extendedFunction = function() {
return this.valueSetOnInit;
};
ExtendedModule.prototype.genericFunction = function() {
return 'OVERWRITTEN';
};
return ExtendedModule;
}(window.jQuery, PublicModule ));
var four = new ExtendedModule({
valueSetOnInit: 'THIS IS FOUR',
extendedVar: 'NYXNYXNYX'
});
// Extended module has its own functionality.
console.assert( four.augmentedFunction() === 'THIS IS FOUR');
console.assert( four.genericFunction() === 'OVERWRITTEN');
// Non-extending module still has its own functionality.
console.assert( one.genericFunction() === 'THIS IS ONE');
// Extended module has all public and static functionality from the parent.
console.assert( four.getStaticVar() === 'NEW STATIC!');
// All static functionality also affects other that inherit the parent.
four.setStaticVar('UBER STATIC!')
console.assert( one.getStaticVar() === 'UBER STATIC!');
console.assert( two.getStaticVar() === 'UBER STATIC!');
console.assert( three.getStaticVar() === 'UBER STATIC!');
console.assert( four.getStaticVar() === 'UBER STATIC!');