ref – https://toddmotto.com/mastering-the-module-pattern/
1 – create IIFE
The module pattern’s basic premise is that a IIFE is created, and thus, creates a closure.
In other words, it declares a function, which then calls itself immediately.
These are also known as Immediately-Invoked-Function-Expressions
1 2 3 |
(function () { // code })(); |
The function creates a new scope, and thus, privacy.
JavaScript doesn’t have privacy, but creating new scope emulates this when we wrap all our functional logic inside them.
The idea then is to return only the parts we need, leaving the other code out of the global scope.
2 – namespace
After creating new scope, we need to namespace our code so that we can access any methods we return. Let’s create a namespace for our anonymous Module.
1 2 3 |
var Module = (function () { // code })(); |
We then have Module declared in the global scope, which means we can call it wherever we like, and even pass it into another Module.
3 – private methods
Private methods are anything you don’t want users/devs/hackers to be able to see/call outside the scope they’re in.
To make our methods inaccessible outside of that scope:
1 2 3 4 5 6 7 |
var Module = (function () { var privateMethod = function () { // do something }; })(); |
The privateMethod is declared inside the new scope. If we were to attempt calling it anywhere outside of our module, we’ll get an error thrown and our JavaScript program will break!
Another example. We declare function expression variable privateMethod. Then a function expression variable publicMethod. publicMethod’s anonymous function definition involves calling privateMethod.
Then it gets returned in an object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var Module = (function () { var privateMethod = function (message) { console.log(message); }; var publicMethod = function (text) { privateMethod(text); }; return { publicMethod: publicMethod }; })(); // Example of passing data into a private method // the private method will then `console.log()` 'Hello!' Module.publicMethod('Hello!'); |
…of course, you can use these public methods to manipulate private properties.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var Module = (function () { var privateArray = []; var publicMethod = function (somethingOfInterest) { privateArray.push(somethingOfInterest); }; return { publicMethod: publicMethod }; })(); |
4 – giving access to outside using “return”
The object literal syntax goes something like this:
1 2 3 4 5 6 7 8 9 10 11 12 |
var myObjLiteral = { defaults: { name: 'Todd' }, someMethod: function () { console.log(this.defaults); } }; // console.log: Object { name: 'Todd' } myObjLiteral.someMethod(); |
Using the same syntax, we want to return an object so that others can access whatever we decide to give them.
The methods bound to the Object will be accessible from the Module’s namespace.
One way to do this is via
anonymous Object Literal return
1 2 3 4 5 6 7 8 |
var Module = (function () { return { publicMethod: function () { // code } }; })(); |
And hence we provide access to functionality like so:
1 |
Module.publicMethod(); |
..and thus, if you have a private method somewhere, you’ll have to use the public method to access that private
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
var Module = (function () { var privateMethod = function () {}; // anonymous object literal return { // with properties added as functions. These function properties are then accessed globally. publicMethodOne: function () { // I can call `privateMethod()` you know... }, publicMethodTwo: function () { }, publicMethodThree: function () { } }; })(); |
Locally Scoped Object
Another way is to locally create an object. Then set this object up by adding function attributes to it. Finally, when its complete, we return this object
1) create empty literal object on the local stack
2) add properties to it that are functions and variables
3) return this literal object
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
var Module = (function () { // locally scoped Object var myObject = {}; // declared with `var`, must be "private" var privateMethod = function () {}; myObject.someMethod = function () { // take it away Mr. Public Method }; myObject.someMethod2 = function() { }; myObject.someMethod3 = function() { } return myObject; })(); |
Stacked locally Scoped Object Literal
1) create object literal referenced by variable name.
2) declare attributes inside this object literal
3) return object literal by returning its variable reference.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var Module = (function () { var privateMethod = function () { }; var myObject = { someMethod: function () { }, anotherMethod: function () { } }; return myObject; })(); |
Revealing Module Pattern
1) declare function expression variables that references anonymous functions
2) return object literal with properties that references the function expression varaibles.
In this way, we reveal public pointers to methods inside the Module’s scope.
Declare functions via named references so that you don’t get hoisting mixups. Then, return an object with named properties to those references.
This again, can create a really nice code management system in which you can clearly see and define which methods are shipped back to the Module.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
var Module = (function () { var privateMethod = function () { // private }; var someMethod = function () { // public }; var anotherMethod = function () { // public }; return { someMethod: someMethod, anotherMethod: anotherMethod }; })(); |
Augmenting Modules
Say you have a really nice revealing pattern going like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
var Module = (function () { var privateMethod = function () { // private }; var someMethod = function () { // public }; var anotherMethod = function () { // public }; return { someMethod: someMethod, anotherMethod: anotherMethod }; })(); |
You got your function expressions declared. You return a nice literal object with properties referencing your expressions.
But…what if you want to extend this module? But from a third-party, and not add anything into it anymore.
In other words, we want to add a “thirdMethod”, but not mess with the current Module implementation.
Solution:
Let’s create another Module named ModuleTwo, and pass in our Module namespace, which gives us access to our Object to extend:
1 2 3 4 5 |
var ModuleTwo = (function (Module) { // access to `Module` })(Module); |
Hence, its basically creating a singleton, and passing in another singleton via injection. We then add a property for a function expression. Then we simply return Module.
1 2 3 4 5 6 7 8 9 |
var ModuleTwo = (function (Module) { Module.extension = function () { // another method! }; return Module; })(Module || {}); |
You’ll notice I’ve passed in Module || {} into my second ModuleTwo, this is incase Module is undefined – we don’t want to cause errors now do we ;). What this does is instantiate a new Object, and bind our extension method to it, and return it.
Private Naming Conventions
make sure to label private properties with underscore
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var Module = (function () { var _privateMethod = function () { // private stuff }; var publicMethod = function () { _privateMethod(); }; return { publicMethod: publicMethod }; })(); |