How to Use Revealing Module Pattern in JavaScript

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:

  1. 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



Leave a reply



Submit