So far you’ve learned a great deal about the module pattern and how to use it. What you haven’t done is take a deep dive into its structure. Understanding why its syntax is the way it is will help you feel more comfortable when incorporating the module pat- tern in your single-page application. In this section, you’ll dissect the module pattern to see how it does what it does.
Let’s start by revisiting the original boilerplate structure to have it fresh in your mind:
var moduleName = (function() { return {
};
})();
This formula is the skeleton for the basic module pattern.
3.3.1 Achieving privacy
JavaScript has only two types of scope: local and global. Declarations made inside a function are local (private), and those made outside any function are global (public).
Because you can’t explicitly mark variables and functions as public or private, you’re left with only their scope to work with. That narrows things down a great deal.
The only way to achieve privacy in JavaScript is to make your declarations locally within a function. Figure 3.10 highlights the anonymous function of the pattern, which internalizes the module’s functionality.
As you may recall from our discussion of the module pattern in the previous sec- tion, this ability to internalize code is essential to achieve its benefits, such as avoiding name conflicts, protecting your code’s integrity, and providing managed access to the module’s functionality.
3.3.2 Creating the public API
You create the public API of a module by combining a few techniques. These tech- niques make for unusual syntax but work perfectly together to give the desired effect.
RETURN AN OBJECT LITERAL
Instead of a simple value, such as true or 3, an object is returned in your module pat- tern (see figure 3.11).
var moduleName = ( function() { var someVar;
function someFunction(){
} return {
someFunction : someFunction };
} ) ();
Outer function
Code local to this function is not accessible outside the module
Figure 3.10 The outer function of the pattern creates a local scope for variables and functions. This gives the module a way to achieve privacy for internal code.
var moduleName = ( function() { var someVar;
function someFunction(){
} return {
someFunction : someFunction };
} ) ();
The functions here give us regulated access to the module.
Figure 3.11 An object literal is returned. Its functions have access to the module’s internal variables and functions. This gives calling code regulated access to the module’s functionality.
71 The module pattern dissected
The returned object can have any assortment of variables and functions, but functions are what allow you to expose behavior. That’s why you typically see only functions available in the returned object.
The object literal is favored for the returned object, because its syntax provides a nice facility to define an object in a single, hierarchical fashion without the need for the new keyword. As you saw earlier in the revealing module pattern, the exposed functions are merely pointers to the inner functions.
CAUSE THE FUNCTION TO RETURN IMMEDIATELY
Normally when a function is declared, it doesn’t return until it’s invoked by other code calling it. In the case of the module pattern, you need the variable you’re using for the module’s namespace to point to the returned object literal, not the function itself. This is accomplished by adding the trailing set of parentheses to the structure (see figure 3.12).
Without the trailing parentheses, the variable will be assigned the entire function, not the object being returned. With them, however, the anonymous function is
Refresher on object literals
As a refresher, let’s look at object literals. In JavaScript, you can create objects with the new keyword, with the Object.create() function (ECMAScript 5), or with literal notation (also referred to as an object initializer). With literal notation, the object is defined using curly braces. Its properties and values are in the form of name-value pairs, separated with a colon. You’ll also need to put a comma after each pair except the last one. Values can contain variables, functions, or other objects. Here’s an example:
var employee = {
firstName : "Bob", lastName : "Jones",
deptInfo : { dept : "Accounting", bldg : "C Building", floor : "1st floor"
},
getFullName : function(){
return this.firstName + " "
+ this.lastName;
}, getDeptInfo : function(){
return this.deptInfo.dept + ", "
+ this.deptInfo.bldg + ", "
+ this.deptInfo.floor;
} }
console.log(employee.getFullName() + ": " + employee.getDeptInfo());
Begin object declaration Declare variables
Nest objects
Declare functions
End object declaration
Prints “Bob Jones: Accounting, C Building, 1st floor”
invoked immediately, returning the object literal to the assigned variable. This is referred to as an immediately invoked function expression, or IIFE.
A CLOSURE IS FORMED
In order for this entire collection of techniques that are used to form this pattern to work, any private variables or functions referenced/in scope by the returned object lit- eral can’t be garbage collected. If they were, you’d get errors when trying to use the API. But a special situation occurs when your object literal is assigned to the module- Name variable. The object literal functions are now available for use, so they can’t be garbage collected. Because these functions in the object literal also have references to internal private objects, those objects can’t be garbage collected either. As you learned in our definition of a closure, this is possible because all functions have a scope that references an outer lexical scope. It’s the closure that keeps the internal functionality alive long after the IIFE has finished executing (see figure 3.13).
In holding onto their references, the module’s inner functions can continue to safely operate without becoming undefined when the outer function finishes executing.
var moduleName = ( function() { var someVar;
function someFunction(){
} return {
someFunction : someFunction };
} ) ();
This causes
immediate invocation.
Figure 3.12 The trailing parentheses cause the anonymous function of the module pattern to be invoked immediately, returning the object literal.
var moduleName = ( function() { var someVar;
function someFunction(){
} return {
someFunction : someFunction };
} ) ();
The closure keeps objects referenced in the module’s outer scope from being garbage-collected.
Figure 3.13 A closure keeps any variables or functions referenced in the IIFE alive, even after execution.
73 The module pattern dissected
3.3.3 Allowing for global imports
The trailing parentheses also give you a way to declare items you want passed into the anonymous function via its parameters. In module terms, these external objects you’re bringing inside for internal use are called imports.
Using this facility to import global variables into the module is a common practice.
It not only makes it clearer to someone reading the code what’s being used but also helps speed up the variable resolution process for the interpreter. Finally, it allows you to alias a global variable, if desired, within the scope of the function. Take, for example, jQuery:
var moduleName = (function($) { function init() {
$("#div-name").html("Hello World");
} return {
init : init };
})(jQuery);
The $ is the way most of us like to reference jQuery. It’s much easier than typing out jQuery all the time. But many libraries out there also want to use $ in their code.
Because you’re specifically aliasing jQuery locally within this module, there’s no chance for the $ in this instance to conflict with the $ from another library.
3.3.4 Creating the module’s namespace
The final part of the module pattern is the establishment of its namespace. This namespace gives you a way to call the module’s public API, as well as somewhere to assign any submodules that may be desired.
In JavaScript, a function can be declared or assigned as an expression. You just read that the outer function of the module pattern is an IIFE. The assignment of the immediately invoked anonymous function to a variable not only gives you a pointer to the returned object literal but also creates the module’s name. It also defines the mod- ule’s namespace if submodules are attached (see figure 3.14).
jQuery aliased as $ via the function parameters
jQuery imported
Creates a namespace for the module
var moduleName = ( function() { var someVar;
function someFunction(){
} return {
someFunction : someFunction };
} ) ();
Figure 3.14 This assignment creates the module’s namespace.
TIP The parentheses around the IIFE aren’t required because this is a func- tion expression. This visually establishes a boundary for the module (see fig- ure 3.15).
And with that final note, your dissection of the module pattern is complete. As you can see, each part of the pattern has a purpose. The end result from incorporating this pattern is that your code base can remain clean, purposeful, and able to grow with your project.