Papa's Perspective
Effective JavaScript Tips
JavaScript can be unwieldy. But using techniques like separation and Revealing Module Pattern can get it under control.
Welcome to the first article of my new column, Papa's Perspective -- and thanks for reading it!
As this is the first of the series (and because my editor ordered me to introduce myself), I'll spend a quick moment sharing a little about me and my vision for this column. Some of you may know me through my books, articles, watching Silverlight TV on Channel 9, speaking at conferences or on Twitter (@john_papa). If you do, you know that I value good development practices, speaking in terms everyone can relate to and being casual.
JavaScript Behaving Badly?
JavaScript gets a bad rap sometimes for being a poor development language. Global variables that can easily be confused with the wrong scope, 3,000-line-long .js files, and cats and dogs living together can all contribute to the utter chaos that makes maintaining JavaScript difficult. But it doesn't have to be that way. I'll say it plainly: JavaScript can be productive and fun.
This month I'll discuss a common technique to separate structure (HTML), behavior (JavaScript) and presentation (CSS) with modern Web development. This type of separation is commonly accepted as a good practice with other technologies. I'll show a simple timer application and offer a few tips to make your JavaScript more manageable.
Separation
First let's take a look at the sample application (Figure 1), a simple timer with buttons for starting, pausing and resetting. The code for this article has a file named TimerAllInOneFile.html, which contains the HTML structure for the timer, the CSS that styles it to look like Figure 1 and the JavaScript to make it function. The file does a pretty good job of separation, but this is a simple example with only a few dozen lines of HTML, CSS and JavaScript.
|
Figure 1. A simple JavaScript timer. |
The problem with this approach is that the code can easily become more difficult to manage. For example, scripts could be created in line with HTML structure or anywhere in the HTML file. In general I find it easier to maintain and debug code when I know the JavaScript is in one place separated from the HTML. Most browsers' debuggers support debugging JavaScript files, but they don't all support debugging script code in HTML files:
<div id="buttonSurface" style=
"width: 236px; margin: auto;">
<div id="startButton" class="Button ButtonStart"
onclick="startTimer();"></div>
<div id="pauseButton" class="Button ButtonPause"
onclick="pauseTimer();"></div>
<div id="clearButton" class="Button ButtonClear"
onclick="clearTimer();"></div>
</div>
You may notice that the buttonSurface DIV has an embedded style. This is an easy trap to fall into when all of your code is in one file. Having embedded styles, styles in the same page in a STYLE tag and linked styles can make it difficult to diagnose problems with presentation. Sometimes there are valid reasons to override styles, but as a general rule I recommend keeping the styles in one place (my preferred location is their own CSS file).
You can see the separation of the HTML, CSS and JavaScript in the three files: Timer.html, Styles.css and Timer.js.
I made two other separation changes as well. First, I removed the embedded style on buttonSurface and moved it to Styles.css, which is arguably more maintainable. I also removed the embedded event handlers and assigned them in code in the Timer.js file, as shown here:
function init(startButton, pauseButton, clearButton) {
document.getElementById(startButton).
addEventListener(
"click", startTimer, false);
document.getElementById(pauseButton).
addEventListener(
"click", pauseTimer, false);
document.getElementById(clearButton).
addEventListener(
"click", clearTimer, false);
displayTimer();
};
The Fight Against Global Variables
All JavaScript variables share a single global namespace. They're created whenever a variable is explicitly declared within the global scope or whenever a variable is implicitly declared. I'm no fan of implicit declaration of variables, because they make debugging and maintenance very difficult. This can make it easier to collide with same-named variables (your own or ones in other script file code) and to lose track of the lifetime.
Figure 2 shows the Internet Explorer 9 debugger displaying the functions and variables used by Timer.html and Timer.js, all of which are in the global namespace. It's conceivable that the HTML page might use another script file that might have a variable named init, seconds or any of the other members listed in Figure 2. There are two easy ways to avoid globals: declaring variables within your own namespace and enclosing your variables within a module.
[Click on image for larger view.] |
Figure 2. The Internet Explorer 9 debugger, displaying the program's functions and variables. |
Namespaces are easy to create and often used by popular libraries like jQuery (which uses $) and KnockoutJS (which uses ko). For my main application, I like to create a namespace called my, which can be done easily using the following code:
var my = my || {};
For simpler apps I don't always use namespaces. But I do use modules quite often, specifically the Revealing Module Pattern.
The Revealing Module Pattern
The Revealing Module Pattern helps encapsulate similar logic and avoid polluting the global namespace. The idea is simply to wrap all related logic in a module and only expose or reveal the parts of it that need to be accessible outside of the module. The basic way to use this pattern is to create a function that wraps all of the JavaScript logic, and return an object from this function that contains the functions that need to be accessible outside your module.
This pattern is quite easy to implement. Simply create a variable and set it to an empty function. Then put your JavaScript code inside that function, as shown here:
var TimerRMP = function () {
/* your JavaScript code goes here */
}();
As an example, take a look at the complete code in TimerRMP.js. The same JavaScript in Timer.js has been copied to TimerRMP.js, then used inside of the function that's set to the variable TimerRMP.
The following code shows the beginning of TimerRMP.js, which specifically demonstrates how the TimerRMP variable is declared. Notice TimerRMP is declared and set to be a function, and then all the code below it is pretty much the same:
var TimerRMP = (function (window) {
var
timerId = -1,
interval = 25,
ms = 0,
seconds = 0,
minutes = 0,
startTimer = function () {
if(timerId === -1) {
timerId = window.setInterval(
"TimerRMP.turnTimerOn()", interval);
}
},
The pattern is called Revealing because the module reveals only the functions returned from the object. Notice that the only functions exposed by the function are init and turnTimerOn.
This means that the only variable added to the global namespace is TimerRMP, and the only functions exposed are TimerRMP.init and TimerRMP.turnTimerOn. This is a dramatic reduction from all the global namespace pollution in Timer.js. Code outside of the module can't access the other variables and functions because they're now hidden inside the module's enclosure:
return {
init: init,
turnTimerOn: turnTimerOn
};
The TimerRMP variable is set immediately because the function is called immediately (self- instancing), as shown in the last line of the TimerRMP.js code:
} (window));
Notice that window is being passed into the function. This allows the window object to be explicitly set to a variable inside of the function and thus avoids any assumption that that variable exists. Some say this is unneeded, while others like how clean it is. Also, if you prefer to turn on the strict option for your JavaScript, you can add the "use strict" to the first line inside of your function.
Benefits of Separation and the Revealing Module Pattern
I've found it valuable to use separation patterns, whether it be Silverlight with Model-View-ViewModel, ASP.NET with MVC or even for unit testing. I don't need a fancy acronym for JavaScript separation to know it's a good thing for maintainability, testing and debugging. Along with separation, I find that the Revealing Module Pattern is easy to follow, reduces variables in the global namespace and makes it simple to encapsulate logic in a way that makes sense. Plus, it keeps JavaScript clean. And that is Papa's Perspective.
About the Author
John Papa is a Microsoft Regional Director and former Microsoft technical evangelist. Author of 100-plus articles and 10 books, he specializes in professional application development with Windows, HTML5, JavaScript, CSS, Silverlight, Windows Presentation Foundation, C#, .NET and SQL Server. Check out his online training with Pluralsight; find him at johnpapa.net and on Twitter at twitter.com/john_papa.