Skip to content

Instantly share code, notes, and snippets.

@zcaceres
Last active February 1, 2024 21:40
Star You must be signed in to star a gist
Save zcaceres/bb0eec99c02dda6aac0e041d0d4d7bf2 to your computer and use it in GitHub Desktop.
Using the Revealing Module Pattern in Javascript

The Revealing Module Pattern in Javascript

Zach Caceres

Javascript does not have the typical 'private' and 'public' specifiers of more traditional object oriented languages like C# or Java. However, you can achieve the same effect through the clever application of Javascript's function-level scoping. The Revealing Module pattern is a design pattern for Javascript applications that elegantly solves this problem.

The central principle of the Revealing Module pattern is that all functionality and variables should be hidden unless deliberately exposed.

Let's imagine we have a music application where a musicPlayer.js file handles much of our user's experience. We need to access some methods, but shouldn't be able to mess with other methods or variables.

Using Function Scope to Create Public and Private Methods

Let's first see how Javascript's function-level scope can help us create public and private methods.

We can move all functionality inside a function's scope. Then we return an object with the functions that we'd like accessible in other files.

// musicPlayerModule.js

var musicPlayer = function () {
  // Let's make sure no one can directly access our songList
  var songList = ['California Girls', 'California Dreaming', 'Hotel California'];  

  // We'll expose all these functions to the user
  function play () {
    console.log('Im playing the next song!');
  }

  function pause () {
    console.log('Im paused!');
  }

  function addTrackToMusicQueue (track) {
    songList.push(track);
    console.log('I added a song');
  }

  function showNextTrack () {
    console.log('My next track is', songList[0]);
  }

  // Let's hide this function
  function loadSong() {
    filesystem.loadNextSong();
  }

  return {
    playMusic: play,
    pauseMusic: pause,
    showNextTrack: showNextTrack,
    addTrack: addTrackToMusicQueue
  }
}

const musicModule = musicPlayer(); // invoke our musicPlayer to return it's object (module)
musicModule.playMusic(); // 'Im playing the next song!'
musicModule.pauseMusic(); // 'I'm paused!'
musicModule.showNextTrack(); // 'The next track is California Girls'

// Things we can't access...
musicModule.loadSong(); // error: not a function
musicModule.songList.push('White Rabbit'); // undefined error

Now we can access all the methods that we need on our musicModule object. But we can't fiddle with our songList or access the loadSong() method. These are both private.

This works fine. But there's one big problem.

We're using musicPlayer as a namespace to hold our functions. But wait, our musicPlayer is a function that's still exposed to the global scope!

Someone could come along and invoke it again, creating a new musicPlayer. Then we would have multiple instances of musicPlayer floating around, polluting our environment and causing all sorts of confusion.

Hiding Your Module with an IIFE

The best way to avoid exposing your top-level function to the global scope is to wrap everything in an IFFE. An IIFE is an immediately-invoked function expression. That's a mouthful. It just means that we call (invoke) this function as soon as the file is run (immediately).

Since our function has no name, we call it an expression. Since it has no name, it can never be invoked elsewhere.

Here's how that looks:

var musicModule = (function () {
  // Let's make sure no one can directly access our songList
  var songList = ['California Girls', 'California Dreaming', 'Hotel California'];  

  // We'll expose all these functions to the user
  function play () {
    console.log('Im playing the next song!');
  }

  function pause () {
    console.log('Im paused!');
  }

  function addTrackToMusicQueue (track) {
    songList.push(track);
    console.log('I added a song');
  }

  function showNextTrack () {
    console.log('My next track is', songList[0]);
  }

  // Let's hide this function
  function loadSong() {
    filesystem.loadNextSong();
  }

  return {
    playMusic: play,
    pauseMusic: pause,
    showUpNext: showNextTrack,
    addTrack: addTrackToMusicQueue
  }
})(); // our IIFE function (surrounded with parens) is invoked here

musicModule.playMusic(); // 'Im playing the next song!'
musicModule.pauseMusic(); // 'I'm paused!'
musicModule.showUpNext(); // 'The next track is California Girls'
musicModule.loadSong(); // error: not a function
musicModule.songList.push('White Rabbit'); // undefined

Our function-level scope still keeps our methods and variables public and private based on whether we expose them in the return object.

But this time, we avoid the risk that we can invoke our module elsewhere in the code.

In other files we can now use musicModule's' functionality – a well-encapsulated module in our global scope!

@jetjokers24895
Copy link

Thanks you very much!. This is very good for me!

@nakulvijay56
Copy link

Nice explanation, but can someone give the advantage and the disadvantage of using this design pattern. I am just learning this pattern.

@chirag97
Copy link

chirag97 commented Apr 7, 2020

thanks for the subtle and to the point explaination.

@Hoxtygen
Copy link

Hoxtygen commented Apr 17, 2020

Thanks for this piece @zcaceres
Is this a typo The best way to avoid exposing your top-level function to the global scope is to wrap everything in an **IFFE**

@NelzkieCoder
Copy link

Thanks

@isam123
Copy link

isam123 commented Jul 2, 2020

Thanks Man

@akihshen
Copy link

akihshen commented Jul 5, 2020

Thanks

@jdiegosierra
Copy link

I don't understand the advantage of using Hiding Your Module with an IIFE. Can someone show me a code example with the problem?

@omundy
Copy link

omundy commented Sep 17, 2020

Where would you place "use strict"; if using several modules? Inside or outside?

@tiomno
Copy link

tiomno commented Oct 29, 2020

Thanks, neat explanation!

@jdiegosierra, I was playing with this pattern and came up with a rather contrived example, but you can get the idea. Check out the comments. ;)

Revealing Module Pattern without IIFE

Revealing Module Pattern with IIFE

Hope it helps!

@allpro
Copy link

allpro commented Mar 20, 2021

I've used this pattern a looong time, but rarely as a single instance (IIFE). To me it's greatest power is as an instantiable module; each instance having its own 'configuration' and 'state'. It's like a React hook or similar component that provides shared-logic. For me this is a much more common need than a singleton.

Every time you call a revealing module, like const foo = ModuleFunction(config), it's the same as calling const foo = new ModuleFunction(config). The args you pass become the 'configuration' of that specific instance, and you can create as many instances as desired. This is a powerful and convenient way to reuse logic.

For example, I have a FormManager module that creates a form-specific object to manage every aspect of a form's data, including custom validation and caching. I can create as many form-manager instances as needed without worrying about 'bleed' from one instance to another.

A singleton module (IIFE) is useful only in cases where there can be exactly one of something.

@niksolaz
Copy link

thanks for explanation. this is very cool

@iampsjain
Copy link

thanks for explanation. this is very cool

@5hraddha
Copy link

Thanks for the short and to the point explanation of 'Revealing Module Pattern'. I am almost a beginner and learning how to use Web pack as a bundler. But, then I was trying to understand why do we need a bundler like Webpack in the first place. This led me to research how JS works in browsers, the problems we get with scoping and how IIFEs helped in resolving the problem to an extent using 'Revealing Module Pattern'. That is how I got to read your article. It surely helped me. Thanks again!!

@pp-id
Copy link

pp-id commented Nov 13, 2021

Thanks it was actually helpful

@Youssef9798
Copy link

Thanks, it was very smooth and easy words!

@ikram-patah
Copy link

👍
Really good and concise explanation. I think worth explaining that the using the IIFE assignment is a singleton pattern, which takes away the ability to create multiple instances of the module.

@seb-thomas
Copy link

Might be useful to show how to pass args (unless i missed it in my quick look :D )

...
  return {
    funky: (arg)=> console.log(arg),
  };
};

module.funky("Sup");

@EhsanTaba
Copy link

I've used this pattern a looong time, but rarely as a single instance (IIFE). To me it's greatest power is as an instantiable module; each instance having its own 'configuration' and 'state'. It's like a React hook or similar component that provides shared-logic. For me this is a much more common need than a singleton.

Every time you call a revealing module, like const foo = ModuleFunction(config), it's the same as calling const foo = new ModuleFunction(config). The args you pass become the 'configuration' of that specific instance, and you can create as many instances as desired. This is a powerful and convenient way to reuse logic.

For example, I have a FormManager module that creates a form-specific object to manage every aspect of a form's data, including custom validation and caching. I can create as many form-manager instances as needed without worrying about 'bleed' from one instance to another.

A singleton module (IIFE) is useful only in cases where there can be exactly one of something.

My man knows his JS patterns!

@KeerthiMage
Copy link

thanks bro

@nhanitk14dev
Copy link

Thanks. It's easy to apply

@ddziuman
Copy link

Thanks. Am I right that this pattern is actually pretty outdated, because JavaScript today supports ES6 modules, and we always can
"deliberately export" the public identifiers from the modules, and each module itself is considered a separated isolated function? Or am I confusing some terminology here?

@zcaceres
Copy link
Author

zcaceres commented Jan 1, 2024

Thanks. Am I right that this pattern is actually pretty outdated, because JavaScript today supports ES6 modules, and we always can "deliberately export" the public identifiers from the modules, and each module itself is considered a separated isolated function? Or am I confusing some terminology here?

You are correct that this pattern is a bit outdated.

@allpro
Copy link

allpro commented Jan 1, 2024

Am I right that this pattern is actually pretty outdated, because JavaScript today supports ES6 modules

The Revealing Module Pattern is NOT outdated, but the singleton pattern (IIFE) shown in this gist is. As I noted in a previous comment, the instantiable Revealing Module Pattern has always been far more useful...

I've used this pattern a looong time, but rarely as a single instance (IIFE). To me it's greatest power is as an instantiable module; each instance having its own 'configuration' and 'state'. It's like a React hook or similar component that provides shared-logic. For me this is a much more common need than a singleton.

This remains true today, and the pattern works great inside an ES6 module. I have used this pattern multiple times in the past year.

An ES6 module is a singleton, so is an replacement for an IIFE. However because it is a singleton, you need to export a instantiable function if you need multiple, independent instances of the object, each with its own internal state.

React hooks are essentially like the Revealing Module Pattern. You define state and methods inside a function, and return only the specific values and methods desired. Every time you use a hook, a new instance is created. The popularity of hooks illustrates how useful and versatile this pattern is.

However not everyone uses React, and even if you do, not every object should be written as a hook. Hooks can only be used within a React component, but sometimes you want a helper that can be instantiated and used outside a component, and that can continue to exist beyond the lifecycle of a component.

For example, a module may perform asynchronous actions that complete after a component unmounted, and then performs other global actions. A module is good way to do this.

It is also useful to combine an instantiable object with a singleton ES6 module. Common state can exist at the ES6 module level, with instance state maintained inside the Revealing Module instance. This provides a way for one instance to share state with other instances, and for instances to interact with each other.

For example, if one instance loads data useful to all instances, this can be put in the shared scope. An internal instance method can also be placed in the shared scope by one instance while an action is in progress, allowing another instance to interact with with the first instance if a conflicting or superceding action is triggered. Or the common scope could be used to create a 'registry' of all active instances, which allows any one instance to call methods in any other, or all at once.

These are just a few examples of how this pattern is still useful today. It leverages the power of JavaScript closures, so is likely to remain useful for a long time.

@zcaceres
Copy link
Author

zcaceres commented Jan 1, 2024

Am I right that this pattern is actually pretty outdated, because JavaScript today supports ES6 modules

The Revealing Module Pattern is NOT outdated, but the singleton pattern (IIFE) shown in this gist is. As I noted in a previous comment, the instantiable Revealing Module Pattern has always been far more useful...

I've used this pattern a looong time, but rarely as a single instance (IIFE). To me it's greatest power is as an instantiable module; each instance having its own 'configuration' and 'state'. It's like a React hook or similar component that provides shared-logic. For me this is a much more common need than a singleton.

This remains true today, and the pattern works great inside an ES6 module. I have used this pattern multiple times in the past year.

An ES6 module is a singleton, so is an replacement for an IIFE. However because it is a singleton, you need to export a instantiable function if you need multiple, independent instances of the object, each with its own internal state.

React hooks are essentially like the Revealing Module Pattern. You define state and methods inside a function, and return only the specific values and methods desired. Every time you use a hook, a new instance is created. The popularity of hooks illustrates how useful and versatile this pattern is.

However not everyone uses React, and even if you do, not every object should be written as a hook. Hooks can only be used within a React component, but sometimes you want a helper that can be instantiated and used outside a component, and that can continue to exist beyond the lifecycle of a component.

For example, a module may perform asynchronous actions that complete after a component unmounted, and then performs other global actions. A module is good way to do this.

It is also useful to combine an instantiable object with a singleton ES6 module. Common state can exist at the ES6 module level, with instance state maintained inside the Revealing Module instance. This provides a way for one instance to share state with other instances, and for instances to interact with each other.

For example, if one instance loads data useful to all instances, this can be put in the shared scope. An internal instance method can also be placed in the shared scope by one instance while an action is in progress, allowing another instance to interact with with the first instance if a conflicting or superceding action is triggered. Or the common scope could be used to create a 'registry' of all active instances, which allows any one instance to call methods in any other, or all at once.

These are just a few examples of how this pattern is still useful today. It leverages the power of JavaScript closures, so is likely to remain useful for a long time.

Thanks @allpro . I mostly agree with your analysis. But I also think that, in practice, not too many people are writing raw IIFEs if they're working as a typical webdev these days. My experience may be biased by certain frameworks.

@allpro
Copy link

allpro commented Jan 4, 2024

I also think that, in practice, not too many people are writing raw IIFEs if they're working as a typical webdev these days.

@zcaceres Thanks, but I'm not sure you understood my point. An IIFE is a "singleton pattern", which is no where near as useful as an "instantiable module". As I noted...

I've used this pattern a looong time, but rarely as a single instance (IIFE). To me it's greatest power is as an instantiable module

Immediately-instantiating a function/module as a singleton (IIFE) is an option, but this is irrelevant to the code structure inside the module. I have never understood why these two completely separate things are so often conflated when describing the Revealing Module code pattern.

Any JS function that contains private internal methods, maintains private internal state, (optional but common), and reveals a limited public interface as an object literal, is using the Revealing Module Pattern. This is what this pattern is.

A key benefit is that the abstracted external/revealed interface provides 'encapsulation', which adds clarity while improving maintainability and extensibility.

Many modern framework, like React, have popularized this coding pattern, because of the benefits it offers. Many React hooks are written using the Revealing Module Pattern, because they have private internals and return/reveal a limited interface, often as an anonymous object literal. Hooks provide automatic memoization of the instance created, and can use helpers for internal state-persistence, (useState()), but these are just minor framework-specific details.

Hooks replace the previous React pattern of using ES6 Class objects, which expose/reveal all of its state/properties and methods. JS Classes represent the classic Object Pattern. Modern Classes have some extra features, and it is possible to mark some methods private, but this is clumsy and limited compared to a function using the Revealing Module Pattern.

The popularity of Hooks in React is just one example of how this flexible pattern remains valuable today, even if most devs do not realize when they are using it! Plus there are still good uses for plain JS Revealing Module functions. I still create some every year.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment