Is Module Pattern in JavaScript is useful only for singleton creation?
https://softwareengineering.stackexchange.com/questions/309361
-
12-12-2020 - |
Question
Some articles (JavaScript Module Pattern In Depth, Mastering The Module Pattern) describe defining modules in JavaScript like in the snippet below (from Addy Osmani's "Learning JavaScript Design Patterns"):
var testModule = (function () {
var counter = 0;
return {
incrementCounter: function () {
return counter++;
},
resetCounter: function () {
console.log( "counter value prior to reset: " + counter );
counter = 0;
}
};
})();
Module usage:
testModule.incrementCounter();
testModule.resetCounter();
In this case we are using a single instance in the entire code, and it means that this module implementation is useful only if we want to create a singleton.
Is it true or there are other use cases when this Module pattern variation can be used?
Solution
I'd argue no-ish. I'm not sure it's really the "module pattern" if you return anything other than a singleton. But, you can certainly use the same "pattern" to accomplish other things.
The module protects the global scope from the internal variables used to build the return-value; but the return value can itself be a function -- including a constructor.
To that end, if you had a need for some sort of "private static" class member which you'd like all instances of a class to access, the module pattern can accomplish that.
As a silly example, suppose you're building a Counter
class, wherein each individual Counter
needs to track a certain event, but wherein you also want a protected TotalCountEvents
across all Counter
objects, you could do this:
var Counter = (function() {
var totalcount = 0;
var constructor = function() {
var count = 0;
this.increment = function() { count += 1; totalcount += 1; };
this.getCount = function() { return count; };
};
constructor.getTotalCount = function() { return totalcount; };
return constructor;
})();
You can then use it like this:
var counterA = new Counter();
var counterB = new Counter();
counterA.increment(); counterA.increment();
counterB.increment(); counterB.increment(); counterB.increment();
console.log(counterA.getCount(), counterB.getCount(), Counter.getTotalCount());
And you'll see:
> 2 3 5
But, access to the underlying totalcount
will be restricted to instances of Counter
.
Bear in mind, the factory pattern can accomplish the same sort of thing, just with stylistic/syntactic differences. Changing your factory to look like a "module" mostly allows you to create instances with new
... which is taboo in some circles anyway.
OTHER TIPS
You can return anything with the module pattern, it's just a way to avoid global pollution while instantiating a module. It's quite common to see it used to generate functions or constructors, which can then be used as often as needed:
var module = (function (){
...scoped stuff...
return function () {
...do stuff with scoped stuff...
};
}());
elsewhere:
var foo = new module();
var bar = new module(baz);
//or
var fizz = module(buzz);
A module is just an IIFE. If you want to reuse the function, then declare it as a function and reuse it to instantiate multiple modules:
function moduleFactory(...) {
...
return {
...
};
}
var module1 = moduleFactory(1);
var module2 = moduleFactory(2);
At this point you're no longer using the "module pattern", because it's not meant to handle multiple instantiation because an IIFE only returns one value (ignoring ES2015+ spread et all).