Using globals give you the opportunity to have your code overwritten by any other JavaScript added to the page after yours. And that is an opportunity best to be avoided.
The work around is to use the revealing module pattern.
I am a huge fan of the revealing module pattern. It keeps your objects encapsulates and was easier to understand for me as a C# guy.
In this post, we will step through the various JavaScript patterns and ending up at how you can implement the revealing module pattern. You will see the JavaScript syntax to create objects and how the revealing module patterns uses scope to keep private data private and reveals the data you want public. And you can do this without mucking up your globals.
Too Many of Globals
Globals are generally considered to be bad.
Global variable can potentially be modified from anywhere, (unless they reside in protected memory) and any part of the program may depend on it. A global variable therefore has an unlimited potential for creating mutual dependencies, and adding mutual dependencies increases complexity.
Let’s dive into some code. Here is some JavaScript code that you might find.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var current = null; | |
var labels = [ | |
'home' : 'home', | |
'articles' : 'articles', | |
'contact':'contact' | |
]; | |
function init() { | |
}; | |
function show() { | |
current = 1; | |
} | |
function hide() { | |
show(); | |
} |
So what is the value of current? And what happens when you use a library that also has init() as a global function? And how easy is that to debug?
The problem is that access is not contained. Anything in the page can overwrite what you are doing.
Namespace Using Object Literal
A better approach is to place all of your code within a namespace; an object that contains all of your code. It helps to prevent name clashes with JavaScript code written by others.
You use literals to represent values in JavaScript. These are fixed values, not variables, that you literally provide in your script.
A simple example is like this:
var foo = {}; foo.name = "Spock"; foo.greeting = function() { return "Hello " + foo.name; }
Or you can use a different syntax, where you have declared the properties within the brackets.
var foo = { name: "Spock", greeting: function() { return "Hello " + foo.name; } };
Either approach requires greeting refer to foo.name rather than just name.
But this repetition of the object name leads to huge code and is really annoying.
Using object literals, our code now looks like this:
my = { var current = null; var labels = [ 'home' : 'home', 'articles' : 'articles', 'contact':'contact' ]; function init() { }; function show() { my.current = 1; } function hide() { my.show(); } }
Anonymous Module
Another method of creating a namespace is through the use of a self executing function. Nothing is global.
(function() { var current = null; var labels = [ 'home' : 'home', 'articles' : 'articles', 'contact':'contact' ]; function init() { }; function show() { my.current = 1; } function hide() { my.show(); } })();
And while this code executes, there is now way to access it from the outside at all. So you cannot use callbacks or event handlers.
Module Pattern
You can specify what is global and what is not global. Use return keyword to specify what can be accessed from outside of the module.
What You Can Access (Public)
Assign the return value of an anonymous function to your namespace object.
myModule = function () { return { myPublicProperty: "I'm accessible as myModule.myPublicProperty.", myPublicMethod: function () { console.log("I'm accessible as myModule.myPublicMethod."); } }; }(); // the parens here cause the anonymous function to execute and return
The closing curly brace and then the parentheses ()
— this notation causes the anonymous function to execute immediately, return
ing the object containing myPublicProperty
and myPublicMethod
. As soon as the anonymous function returns, that returned object is addressable as myModule
.
What You Cannot Access (Private)
Add “private” methods and variables in the anonymous function prior to the return
statement.
myModule = function () { //"private" variables: var myPrivateVar = "I can be accessed only from within myModule."; //"private" method: var myPrivateMethod = function () { console.log("I can be accessed only from within myModule"); } return { myPublicProperty: "I'm accessible as myModule.myPublicProperty.", myPublicMethod: function () { console.log("I'm accessible as myModule.myPublicMethod."); //Within myProject, I can access "private" vars and methods: console.log(myPrivateVar); console.log(myPrivateMethod()); //The native scope of myPublicMethod is myProject; we can //access public members using "this": console.log(this.myPublicProperty); } }; }(); // the parens here cause the anonymous function to execute and return
In the preceding example, the myModule object you return
from an anonymous function is an object with two members. These members are addressable from within myModule
as this.myPublicProperty
and this.myPublicMethod
. From outside of myModule
, these public members are addressable as myModule.myPublicProperty
and myModule.myPublicMethod
.
The private variables myPrivateProperty
and myPrivateMethod
can only be accessed from within the anonymous function itself or from within a member of the return
ed object. They are preserved, despite the immediate execution and termination of the anonymous function, through the power of closure — the principle by which variables local to a function are retained after the function has returned. As long as myModule
needs them, your two private variables will not be destroyed.
Revealing Module Pattern
You can make this easier to read using the Revealing Module Pattern. Here you put all of the functions, methods and variables above the return, and reveal the functions, methods, and variable you need following the return.
For example:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
myModule = function () { | |
//"private" variables: | |
var myPrivateVar = "I can be accessed only from within myModule."; | |
//"private" method: | |
var myPrivateMethod = function () { | |
console.log("I can be accessed only from within myModule"); | |
} | |
myPublicProperty: "I'm accessible as myModule.myPublicProperty.", | |
myPublicMethod: function () { | |
console.log("I'm accessible as myModule.myPublicMethod."); | |
//Within myProject, I can access "private" vars and methods: | |
console.log(myPrivateVar); | |
console.log(myPrivateMethod()); | |
//The native scope of myPublicMethod is myProject; we can | |
//access public members using "this": | |
console.log(this.myPublicProperty); | |
} | |
return { | |
// This section is what makes the property and method | |
// publicly available | |
myPublicProperty: myPublicProperty; | |
myPublicMethod: myPublicMethod; | |
}; | |
}(); // the parens here cause the anonymous function to execute and return |
The public properties and methods are available because they are declared in the return. This also lets you keep the syntax consistent within your function.
References
Values, variables, and literals
Why is it bad to make elements global variables in Javascript? on Stackoverflow
Creating namespaces in JavaScript by Kevan Stannard
A JavaScript Module Pattern by Eric Miraglia
JavaScript Best Practices by Christian Heilmann
Spotting Outdated JavaScript by David B. Calhoun
Revealing Prototype Pattern – Techniques, Strategies and Patterns for Structuring JavaScript Code by Dan Wahlin
One thought on “Object JavaScript – Namespaces, Anonymous Module, Revealing Module Pattern”
Comments are closed.