Techniques, Strategies and Patterns for Structuring JavaScript Code
JavaScript has come a long way since the mid-90s when I first started working with it in Netscape 3 and Internet Explorer 3. Back in the day I thought JavaScript was a painful to use but over the years I’ve learned to love it and appreciate what it offers as a language. JavaScript is quite flexible and can perform a wide variety of tasks on both the client-side and server-side. In fact, I used to prefer it to VBScript on the server-side when writing classic ASP applications and today we have server-side frameworks such as Node.js that are JavaScript based. With the rise of HTML5 and new features such as the Canvas API and SVG JavaScript is more important than ever when building applications. As applications use more JavaScript it’s important that the code is structured in a way that’s easy work with and maintain.
Although JavaScript isn’t designed with the concept of classes or object oriented programming in mind as with C# or Java, with a little work you can achieve similar results. In this series of posts I’ll discuss a few popular techniques/strategies/patterns (pick whichever term you prefer) for structuring JavaScript to encapsulate functionality much like classes do, hide private members, and provide a better overall re-use strategy and maintenance story in applications.
I’ll use a calculator example throughout the posts to demonstrate different techniques that can be used for structuring JavaScript code. I decided on a calculator since it provides a simple starting point that everyone understands without needing a detailed explanation. An example of the calculator interface is shown next.
I use specific add, subtract, multiply, divide functions in the code that’ll be shown mainly because I tend to avoid eval() and I wanted to add enough functions to realistically demonstrate why taking the time to learn JavaScript code structuring techniques and patterns is worthwhile. In this first post in the series we’ll look at the technique most people use when writing JavaScript code, examine the role of closures, and discuss different ways to define variables. After that I’ll add additional posts covering the patterns mentioned earlier.
Function Spaghetti Code
Most people (including myself) start out writing JavaScript code by adding function after function into a .js or HTML file. While there’s certainly nothing wrong with that approach since it gets the job done, it can quickly get out of control when working with a lot of code. When lumping functions into a file, finding code can be difficult, refactoring code is a huge chore (unless you have a nice tool like Resharper 6.0), variable scope can become an issue, and performing maintenance on the code can be a nightmare especially if you didn’t originally write it. The following code sample demonstrates using the function based approach to create a simple calculator that I threw together. It’s not perfect, but it gets the job done for the pattern samples that will be posted later.
window.onload = function () { eqCtl = document.getElementById('eq'); currNumberCtl = document.getElementById('currNumber'); }; var eqCtl, currNumberCtl, operator, operatorSet = false, equalsPressed = false, lastNumber = null; function add(x,y) { return x + y; } function subtract(x, y) { return x - y; } function multiply(x, y) { return x * y; } function divide(x, y) { if (y == 0) { alert("Can't divide by 0"); return 0; } return x / y; } function setVal(val) { currNumberCtl.innerHTML = val; } function setEquation(val) { eqCtl.innerHTML = val; } function clearNumbers() { lastNumber = null; equalsPressed = operatorSet = false; setVal('0'); setEquation(''); } function setOperator(newOperator) { if (newOperator == '=') { equalsPressed = true; calculate(); setEquation(''); return; } //Handle case where = was pressed //followed by an operator (+, -, *, /) if (!equalsPressed) calculate(); equalsPressed = false; operator = newOperator; operatorSet = true; lastNumber = parseFloat(currNumberCtl.innerHTML); var eqText = (eqCtl.innerHTML == '') ? lastNumber + ' ' + operator + ' ' : eqCtl.innerHTML + ' ' + operator + ' '; setEquation(eqText); } function numberClick(e) { var button = (e.target) ? e.target : e.srcElement; if (operatorSet == true || currNumberCtl.innerHTML == '0') { setVal(''); operatorSet = false; } setVal(currNumberCtl.innerHTML + button.innerHTML); setEquation(eqCtl.innerHTML + button.innerHTML); } function calculate() { if (!operator || lastNumber == null) return; var currNumber = parseFloat(currNumberCtl.innerHTML), newVal = 0; //eval() would've made this a whole lot simpler //but didn't want to use it in favor of a more //"robust" set of methods to demo patterns switch (operator) { case '+': newVal = add(lastNumber, currNumber); break; case '-': newVal = subtract(lastNumber, currNumber); break; case '*': newVal = multiply(lastNumber, currNumber); break; case '/': newVal = divide(lastNumber, currNumber); break; } setVal(newVal); lastNumber = newVal; }
Although I’m quite confident this code can be refactored in some manner within the functions (it was thrown together quickly for demonstration purposes), you can see it performs a few key calculator features such as handling arithmetic operations, detecting when operators are selected and performing calculations. Although everything shown in the code is standard JavaScript and works fine, as the number of functions grows things can quickly get out of hand. You can put the code in a file named calculator.js and then use it in as many pages as you’d like. However, if you come from an object oriented language you’d probably like to encapsulate the functionality into the equivalent of a “class”. Although classes aren’t supported directly in JavaScript, you can emulate the functionality using different types of patterns.
Another problem with this type of code is that any variables defined outside of functions are placed in the global scope by default. The script shown above adds 6 variables to the global scope (the functions get added as well by the way). This means that they can more easily be stepped on or changed by anything in your script or another script that may be using the same variable names. It’d be nice to localize the global variables and limit their scope to avoid variable and scope conflicts. Fortunately that can be done using functions. However, if you define a variable in a function it goes away after the function returns right? That problem can be remedied by using closures which are an important part of the JavaScript patterns that I’ll cover in this series.
What are Closures?
The patterns that will be discussed in this series rely on a key concept in JavaScript called closures. If you’re new to closures then I highly recommend reading the JavaScript Closures for Dummies article since it provides a nice overview along with several samples. Closures are important because they allow stateful objects to be created without relying on variables defined in the global scope. By using closures you can emulate features found in the class approach taken by object-oriented languages such as C# and Java.
A closure is created when a function has variables that are bound to it in such a way that even after the function has returned, the variables stick around in memory. So what’s the magic that allows variables to be “bound” in such a way that they stick around even after a function returns? The answer is nested functions. When one function has a nested function inside of it, the nested function has access to the vars and parameters of the outer function and a “closure” is created behind the scenes. Douglas Crockford explains this with the following quote:
“What this means is that an inner function always has access to the vars and parameters of its outer function, even after the outer function has returned.”
To better understand closures examine the following code representing a standard JavaScript function without any closures:
function myNonClosure() { //variable will not be stored in a closure between calls //to the myNonClosure function var date = new Date(); return date.getMilliseconds(); }
When the myNonClosure function is invoked the date variable will be assigned a new Date object. The function then returns the milliseconds. Calling the function multiple times will cause the date variable to be assigned a new value each time. This is of course the expected behavior. With a closure, a variable can be kept around even after a function returns a value. An example of a function named myClosure() that creates a closure is shown next:
//closure example function myClosure() { //date variable will be stored in a closure //due to the nested function referencing it var date = new Date(); //nested function
return function () { var otherDate = new Date(); return "Closure variable value for milliseconds: <span class='blue'>" + date.getMilliseconds() + "</span><br>Non closure variable value for milliseconds: <span class='red'>" + otherDate.getMilliseconds() + "</span>"; }; }
Looking through the code you can see that a variable named date is assigned a Date object which is similar to the variable shown earlier. However, notice that myClosure returns a nested function which references the date variable. This creates a closure causing the date variable to be kept around even after a value has been returned from the function. To see this in action the following code can be run:
window.onload = function () { //Using a closure var output = document.getElementById('Output'), closure = myClosure(); output.innerHTML = closure(); setTimeout(function() { output.innerHTML += '<br><br>' + closure(); }, 1500); };
The code first references the myClosure() function and stores it in a variable named “closure”. The nested function is then called with the closure() call (note that the name “closure” could be anything – I chose it simply to make its purpose obvious) which invokes the function and returns the current milliseconds. Next, a timeout is set to execute closure() again after 1.5 seconds have elapsed. The results of running the code are shown next. They demonstrate how the date variable is kept around even across multiple calls to the myClosure function. This is an important feature of JavaScript that is leveraged by the different patterns that will be shown.
Here’s a final example of a closure for you to study. It follows one of the patterns that will be shown later in this series. Note that the myNestedFunc variable references a nested function that accesses the date variable.
var myClosure2 = function () { var date = new Date(), myNestedFunc = function () { return "Closure for myNestedFunc: " + date.getMilliseconds(); }; return { myNestedFunc: myNestedFunc }; } ();
This code is called using the following syntax.
output.innerHTML += '<br><br>'+ myClosure2.myNestedFunc();
Defining Variables
Defining variables in JavaScript is one of the more simple aspects of the language. However, there are a few different ways to do it. For example, the following code is completely valid and what most people getting started with JavaScript do:
var eqCtl; var currNumberCtl; var operator; var operatorSet = false; var equalsPressed = false; var lastNumber = null;
Although this code works fine, tools such as JSLint for Visual Studio will let you know to define the variables differently. In the posts that follow you’ll see code similar to the following when defining variables. It only uses the JavaScript var keyword once and then separates variables with a comma. The code ultimately does the same thing as the code above but it reduces the size of the script and is more readable once you get used to it. Here’s an example of defining variables that keeps JSLint and other tools happier when they inspect your code:
var eqCtl, currNumberCtl, operator, operatorSet = false, equalsPressed = false, lastNumber = null;
In the next post I’ll discuss the Prototype Pattern and how you can use it to convert function spaghetti code into a more structured object.
Demos of all the patterns covered in this series can be downloaded below.
Download Code
Pluralsight Course - Structuring JavaScript Code in HTML5 Applications
If you're interested in additional information about structuring JavaScript code check out my Pluralsight course. Here's a sample from the course covering closures.Demo - Working with Closures in JavaScript