How to use Revealing module pattern in JavaScript
A small example:
var revealed = function(){
var a = [1,2,3];
function abc(){
return (a[0]*a[1])+a[2];
}
return {
name: 'revealed',
abcfn: abc
}
}();
in the anonymous function that is initiated to give revealed
a value, a
and abc
are private to that function. What the function returns is an object literal with a name
property and a abcfn
property, which is a reference to the abc function
. The abc function
uses the private variable a
. This can all be done thanks to the use of closures (everything within the scope of a function can be referenced by everything else in that same function).
Revealed usage:
alert(revealed.name); //=> 'revealed'
alert(revealed.abcfn()); //=> 5 (1*2+3)
JavaScript design pattern: difference between module pattern and revealing module pattern?
There are at least three different ways to implement the Module Pattern, but the Revealing Module Pattern is the only Module Pattern descendant that has an official name.
The Basic Module Pattern
The Module Pattern must satisfy the following:
- Private members live in the closure.
- Public members are exposed in the return object.
But there's a lot of ambiguity in this definition. By resolving the ambiguity differently, you get variants of the Module Pattern.
The Revealing Module Pattern
The Revealing Module Pattern is the most famous and most popular of the Module Pattern variants. It has a number of advantages over the other alternatives, such as
- Rename public functions without changing function body.
- Change members from public to private or vice versa by modifying a single line, without changing the function body.
The RMP satisfies three additional conditions in addition to those in the original:
- All members, whether public or private, are defined in the closure.
- The return object is an object literal with no function definitions. All right hand side expressions are closure variables
- All references are via the closure variables, not the return object.
The following example shows how it's used
var welcomeModule = (function(){
var name = "John";
var hello = function(){ console.log("Hello, " + name + "!");}
var welcome = function() { console.log( hello() + " Welcome to StackOverflow!");}
return {
name: name,
sayHello: hello,
sayWelcome: welcome
}
})();
If you wanted to make name
and sayHello
private, you just need to comment out the appropriate lines in the return object.
var welcomeModule = (function(){
var name = "John";
var hello = function(){ console.log("Hello, " + name + "!");}
var welcome = function() { console.log( hello() + " Welcome to StackOverflow!");}
return {
//name: name,
//sayHello: hello,
sayWelcome: welcome
}
})();
The Module Pattern with Object Literal
This is probably the oldest variant of the Module Pattern. Unlike RMP, there's no sexy official name for this variant.
It satisfies the following conditions, in addition to the original:
- Private members are defined in the closure.
- Public members are defined in the return object literal.
- References to public members are via
this
, whenever possible.
In the following example, you can see how, in contrast to RMP, the function definitions are actually in the return object literal, and references to members are qualified by this
.
var welcomeModule = (function(){
return {
name: "John",
sayHello: function(){ console.log("Hello, " + this.name + "!");}
sayWelcome: function() { console.log( this.hello() + " Welcome to StackOverflow!");}
}
})();
Note that unlike RMP, in order to make name
and sayHello
private, the references pointing to name
and sayHello
in the various function body definitions also have to be changed.
var welcomeModule = (function(){
var name = "John";
var sayHello = function(){ console.log("Hello, " + name + "!");};
return {
//name: "John",
//sayHello: function(){ console.log("Hello, " + this.name + "!");}
sayWelcome: function() { console.log( hello() + " Welcome to StackOverflow!");}
}
})();
The Module Pattern with Return Object Stub
This variant also has no official name.
It satisfies the following conditions, in addition to the original:
- An empty return object stub is defined at the beginning.
- Private members are defined in the closure.
- Public members are defined as members of the stub
- References to public members are via the stub object
Using our old example, you can see that public members are directly added to the stub object.
var welcomeModule = (function(){
var stub = {};
stub.name = "John";
stub.sayHello = function(){ console.log("Hello, " + stub.name + "!");}
stub.sayWelcome = function() { console.log( stub.hello() + " Welcome to StackOverflow!");}
return stub;
})();
If you want to make name
and sayHello
private as before, the references to the now-private members have to be changed.
var welcomeModule = (function(){
var stub = {};
var name = "John";
var sayHello = function(){ console.log("Hello, " + name + "!");}
stub.sayWelcome = function() { console.log( hello() + " Welcome to StackOverflow!");}
return stub;
})();
Summary
The differences between the Revealing Module Pattern and the other variants of the Module Pattern is primarily in how public members are referenced. As a result, RMP is much easier to use and modify, which accounts for its popularity. However, these advantages come at a great cost (in my opinion), which Addy Osmani alludes to in his post on the Revealing Module Pattern,
A disadvantage of this pattern is that if a private function refers to a public function, that public function can't be overridden if a patch is necessary. This is because the private function will continue to refer to the private implementation and the pattern doesn't apply to public members, only to functions.
Public object members which refer to private variables are also subject to the no-patch rule notes above.
As a result of this, modules created with the Revealing Module pattern may be more fragile than those created with the original Module pattern, so care should be taken during usage.
and which I've talked about in some other posts.
(Revealing) Module Pattern, public variables and return-statement
Does this actually mean, that I can't have any methods, that handle public properties?
No, it means that you cannot have public variables. var _public
is a variable, and it is not accessible from outside, and when you modify the private variable this will not be reflected in your public ._public
property.
If you want to make things public, use properties:
var a = function() {
var _private = null;
function init() {
_private = 'private';
this._public = 'public';
}
function getPrivate() {
return _private;
}
return {
_public : null,
init : init,
getPrivate : getPrivate,
}
}();
I can manipulate that public property, like a._public = 'public';. But I can't change it from within my object.
You can use this
in the methods of your object, as shown above. Or you use a
to reference the object, or possibly even store a local reference to the object you return. See here for the differences.
Or at least those changes aren't passed through
Yes, because variables are different from properties (unlike in some other languages like Java, and with exceptions for global ones). When you export public: _public
in your object literal, it takes only the current value from the _public
variable and creates a property on the object with it. There is no persistent reference to the variable, and changes to one are not reflected in the other.
Why isn't it possible to just use
return this;
to make everything public? As this should be the context of the self-invoked function, shouldn't it just return eveyrthing in it?
Variables are part of a scope in JavaScript. (Except for the global one) those scopes are not objects accessible to the language.
The this
keyword does not refer to this scope of the function, but to the context that was provided by the call. That can be the base reference in a method call, the new instance in a constructor invocation, or just nothing in a basic function call like yours (or the global window
object in loose mode).
Passing arguments into the revealing Module pattern in Javascript
Do with function inside the return object
var GlobalModule = (function() { return { consoleLog: function(textToOutput) { console.log(textToOutput); } }})();
GlobalModule.consoleLog("Dummy text");
Revealing module pattern combined with ES6 modules
The main purpose of the revealing module pattern is to keep data encapsulated, but the top level of an ES6 module is already private - variables defined in it do not leak to the global scope (unless you assign to the global object explicitly, like window.foo = 'foo'
).
So, in an ES6 module, there's not really any point to the revealing module pattern - feel free to define whatever you want on the top level, and it'll be scoped to the module (and only the module), and then you can explicitly export
whatever needs to be revealed (and nothing else will be undesirably revealed).
JavaScript Revealing Module pattern private variable state
Since privateDomain
is a String, you're not copying / returning the reference, but the value.
Therefore when you're changing the domain using the init
function, it just updates privateDomain
, since domain
has no link to it other than being a copy.
Hope it helps! :)
Idiomatic Revealing Module Pattern for ES6
I read all the time that default exports are preferred
No, they are not. They are simpler and have shorter syntax, and might be used more often (since there are more small, single-export modules), but they are not generally preferred.
Use the right tool for the job. You already know the advantages you want.
Is there an idiomatic pattern for the revealing module pattern with ES6 module syntax?
Yes, Option #1. When you have multiple things to export, always use named exports.
You get both explicit aliasing and tree shaking from the picky syntax
import { foo, bar } from './export-file';
as well as namespaces
import * as baz from './export-file';
Converting single file revealing module pattern javascript to multi-file using import
You could use ES6 modules
.
Steps:
- Create the module file, let's say the file name is 'someModule.js' and add your code in it and export the methods using
ES6
export
.
//some private variables
let private1,
private2;
//some public functions
function addDatatoPrivate1 (data) {
private1 = processData(data);
}
function addDatatoPrivate2 (data) {
private2 = processData(data);
}
//private function processData
function processData(data) {
return data.trim();
}
return {
addDatatoPrivate1: addDatatoPrivate1,
addDatatoPrivate2: addDatatoPrivate2,
}
export {
processData,
addDatatoPrivate1,
addDatatoPrivate2,
}
Now the user of the module can import the module like below.
Using ES6
object destructuring
import {addDatatoPrivate1, addDatatoPrivate2, processData} from './someModule'
addDatatoPrivate1(' some data here ');
addDatatoPrivate2(' some data here2 ');
OR using wild card (*)
import * as moduleInstance from './someModule'
moduleInstance.addDatatoPrivate1(' some data here ');
moduleInstance.addDatatoPrivate2(' some data here2 ');
It's not possible to export a variable privately. Anything exported from module is public by default.
If you want to create separate module for each then you could do something like below.
We could use
ES6
default export to avoid object destructuring.
module1.js
function processData(data) {
return data.trim();
}
export default processData;
module2.js
import processData from './module1';
//some private variables
let private1;
//some public functions
function addDatatoPrivate1 (data) {
private1 = processData(data);
}
export default addDatatoPrivate1;
module3.js
import processData from './module1';
//some private variables
let private2;
function addDatatoPrivate2 (data) {
private2 = processData(data);
}
export default addDatatoPrivate2;
And then you can include these module and some other file.
import addDatatoPrivate1 from './module2';
import addDatatoPrivate2 from './module3';
addDatatoPrivate1(' some data here ');
addDatatoPrivate2(' some data here2 ');
or you could export all of you method in one file so other can import it and use.
index.js
import addDatatoPrivate1 from './module2';
import addDatatoPrivate2 from './module3';
export {
addDatatoPrivate1,
addDatatoPrivate2
}
Related Topics
How to Check If an Element Is Really Visible with JavaScript
What Is "Export Default" in JavaScript
How to Profile JavaScript Execution
What's the Best Way to Break from Nested Loops in JavaScript
JSON.Stringify Doesn't Work with Normal JavaScript Array
Check If All Values of Array Are Equal
Http Ajax Request via Https Page
Wrong React Hooks Behaviour with Event Listener
Addition Is Not Working in JavaScript
Stringify (Convert to JSON) a JavaScript Object with Circular Reference
What Is the Cleanest Way to Get the Progress of Jquery Ajax Request
How to Extract the Hostname Portion of a Url in JavaScript
Jquery Has Deprecated Synchronous Xmlhttprequest
Which Keycode for Escape Key with Jquery
How to Transpose a JavaScript Object into a Key/Value Array
Pretty Printing Xml with JavaScript