Amberley Romo

The Module Pattern in JavaScript

June 06, 2016

Let’s talk about organizing our code. There’s never just one way to accomplish something with code. This is both great, and sort of paralyzing. How do you choose?

Design patterns

Design patterns in JavaScript are documented approaches — or frameworks — for solving problems and structuring code. They are tried and true, reusable, and improve readability — for yourself and others reading your code.

From Addy Osmani’s “Learning JavaScript Design Patterns“–

A pattern is a reusable solution that can be applied to commonly occurring problems in software design – in our case – in writing JavaScript web applications.

Patterns are not an exact solution. It’s important that we remember the role of a pattern is merely to provide us with a solution scheme.

The module pattern is one such pattern. Before we look at this pattern, let’s very briefly refresh on the concept of closure.

Closure

A closure is when a function remembers the context in which it was created, and still has access to local variables, even when that function is executed in a different context — or scope.

For example:

function sayHi(name) {
var greetingText = "Hi there, ";
function speakGreeting(){
return greetingText + name;
}
return speakGreeting()
}
sayHi('Cass');
//=> "Hi there, Cass"

The closure (speakGreeting) still has access to the outer function’s variables (here, greetingText) and parameters (here, name).

Module pattern

Modules help keep our code neatly organized, and optimized for reuse. They also, by making use of closure, allow us to selectively obscure certain details and expose — or make available — selected functionality.

Keeping the concept of closure in mind, we’ll expand our example to reflect a simple implementation of a module.

function sayHi() {
var greetingText = "Hi there, ";
function speakGreeting(name){
return greetingText + name;
}
function shoutGreeting(name){
return greetingText.toUpperCase() + name.toUpperCase();
}
function sayName(name){
return name;
}
return {
speakGreeting: speakGreeting,
shoutGreeting: shoutGreeting,
sayName: sayName
}
}
var speak = sayHi();
speak.speakGreeting('Cass');
//=> "Hi there, Cass"
speak.shoutGreeting('Dean');
//=> "HI THERE, DEAN"
speak.sayName('Crowley');
//=> "Crowley"

The speakGreeting and shoutGreeting functions close over the local variable greetingText.

After executing the outer function sayHi() and assigning the returned object to the variable speak, references to our inner functions speakGreeting, shoutGreeting, and sayName are available as methods on speak.

What we are doing here is creating an API. Let’s look at a slightly modified example that makes that more clear.

var module = (function outer() {
var privateVar = "Hi there, ";
function publicFn1(name){
return privateVar + name;
}
function publicFn2(name){
return privateVar.toUpperCase() + name.toUpperCase();
}
function publicFn3(name){
return name;
}
var publicAPI = {
publicFn1: publicFn1,
publicFn2: publicFn2,
publicFn3: publicFn3
};
return publicAPI;
})();
module.publicFn1('Cass');
//=> "Hi there, Cass"
module.publicFn2('Dean');
//=> "HI THERE, DEAN"
module.publicFn3('Crowley');
//=> "Crowley"

What has changed?

  • We are immediately invoking our outer function and assigning the returned object to module. This is useful when multiple instances aren’t needed
  • Within the outer function, we assign the object we are returning to var publicAPI and return publicAPI. This makes clear to anyone reading our code (ourselves included) that we are exposing these methods

The functional result, however, is essentially the same.

Modules are like lego pieces — little units that can be used once, or over and over, depending on what you need and where it’s needed, to build larger, more complex structures — except you can form any piece you need, to your own specifications.

Further reads and sources:

© 2023 Amberley Romo