You are previewing Maintainable JavaScript.

Maintainable JavaScript

Cover of Maintainable JavaScript by Nicholas C. Zakas Published by O'Reilly Media, Inc.
  1. Maintainable JavaScript
  2. SPECIAL OFFER: Upgrade this ebook with O’Reilly
  3. Introduction
  4. Preface
    1. Conventions Used in This Book
    2. Using Code Examples
    3. Safari® Books Online
    4. How to Contact Us
  5. I. Style Guidelines
    1. 1. Basic Formatting
      1. Indentation Levels
      2. Statement Termination
      3. Line Length
      4. Line Breaking
      5. Blank Lines
      6. Naming
      7. Literal Values
    2. 2. Comments
      1. Single-Line Comments
      2. Multiline Comments
      3. Using Comments
      4. Documentation Comments
    3. 3. Statements and Expressions
      1. Brace Alignment
      2. Block Statement Spacing
      3. The switch Statement
      4. The with Statement
      5. The for Loop
      6. The for-in Loop
    4. 4. Variables, Functions, and Operators
      1. Variable Declarations
      2. Function Declarations
      3. Function Call Spacing
      4. Immediate Function Invocation
      5. Equality
      6. eval()
      7. Primitive Wrapper Types
  6. II. Programming Practices
    1. 5. Loose Coupling of UI Layers
      1. What Is Loose Coupling?
      2. Keep JavaScript Out of CSS
      3. Keep CSS Out of JavaScript
      4. Keep JavaScript Out of HTML
      5. Keep HTML Out of JavaScript
    2. 6. Avoid Globals
      1. The Problems with Globals
      2. Accidental Globals
      3. The One-Global Approach
      4. The Zero-Global Approach
    3. 7. Event Handling
      1. Classic Usage
      2. Rule #1: Separate Application Logic
      3. Rule #2: Don’t Pass the Event Object Around
    4. 8. Avoid Null Comparisons
      1. Detecting Primitive Values
      2. Detecting Reference Values
      3. Detecting Properties
    5. 9. Separate Configuration Data from Code
      1. What Is Configuration Data?
      2. Externalizing Configuration Data
      3. Storing Configuration Data
    6. 10. Throw Your Own Errors
      1. The Nature of Errors
      2. Throwing Errors in JavaScript
      3. Advantages of Throwing Errors
      4. When to Throw Errors
      5. The try-catch Statement
      6. Error Types
    7. 11. Don’t Modify Objects You Don’t Own
      1. What Do You Own?
      2. The Rules
      3. Better Approaches
      4. A Note on Polyfills
      5. Preventing Modification
    8. 12. Browser Detection
      1. User-Agent Detection
      2. Feature Detection
      3. Avoid Feature Inference
      4. Avoid Browser Inference
      5. What Should You Use?
  7. III. Automation
    1. 13. File and Directory Structure
      1. Best Practices
      2. Basic Layout
    2. 14. Ant
      1. Installation
      2. The Build File
      3. Running the Build
      4. Target Dependencies
      5. Properties
      6. Buildr
    3. 15. Validation
      1. Finding Files
      2. The Task
      3. Improving the Target
      4. Other Improvements
      5. Buildr Task
    4. 16. Concatenation and Baking
      1. The Task
      2. Line Endings
      3. Headers and Footers
      4. Baking Files
    5. 17. Minification and Compression
      1. Minification
      2. Compression
    6. 18. Documentation
      1. JSDoc Toolkit
      2. YUI Doc
    7. 19. Automated Testing
      1. YUI Test Selenium Driver
      2. Yeti
      3. PhantomJS
      4. JsTestDriver
    8. 20. Putting It Together
      1. Missing Pieces
      2. Planning the Build
      3. Using a CI System
  8. A. JavaScript Style Guide
    1. Indentation
    2. Line Length
    3. Primitive Literals
    4. Operator Spacing
    5. Parentheses Spacing
    6. Object Literals
      1. Single-Line Comments
      2. Multiline Comments
      3. Comment Annotations
    8. Variable Declarations
    9. Function Declarations
    10. Naming
    11. Strict Mode
    12. Assignments
    13. Equality Operators
    14. Ternary Operator
    15. Statements
      1. Simple Statements
      2. return Statement
      3. Compound Statements
      4. if Statement
      5. for Statement
      6. while Statement
      7. do Statement
      8. switch Statement
      9. try Statement
    16. White Space
    17. Things to Avoid
  9. B. JavaScript Tools
    1. Build Tools
    2. Documentation Generators
    3. Linting Tools
    4. Minification Tools
    5. Testing Tools
  10. Index
  11. About the Author
  12. Colophon
  13. SPECIAL OFFER: Upgrade this ebook with O’Reilly
  14. Copyright
O'Reilly logo

Chapter 4. Variables, Functions, and Operators

The real guts of any JavaScript program are the functions you write to accomplish tasks. Inside the functions, variables and operators are used to move bits around and make things happen. That’s why, after getting the basic formatting of your JavaScript down, it’s important to decide how to use functions, variables, and operators to reduce complexity and improve readability.

Variable Declarations

Variable declarations are accomplished by using the var statement. JavaScript allows the var statement to be used multiple times and nearly anywhere within a script. This usage creates interesting cognitive issues for developers, because all var statements are hoisted to the top of the containing function regardless of where they actually occur in the code. For example:

function doSomething() {

    var result = 10 + value;
    var value = 10;
    return result;

In this code, it’s perfectly valid for the variable value to be used before it was declared, though it will cause result to have the special value NaN. To understand why, you need to be aware that this code is changed by the JavaScript engine to this:

function doSomething() {

    var result;
    var value;

    result = 10 + value;
    value = 10;

    return result;

The two var statements are hoisted to the top of the function; the initialization happens afterward. The variable value has the special value undefined when it’s used on line 6, so result becomes NaN (not a number). Only after that is value finally assigned the value of 10.

One area where developers tend to miss variable declaration hoisting is with for statements, in which variables are declared as part of the initialization:

function doSomethingWithItems(items) {

    for (var i=0, len=items.length; i < len; i++) {

JavaScript up to ECMAScript 5 has no concept of block-level variable declarations, so this code is actually equivalent to the following:

function doSomethingWithItems(items) {

    var i, len;

    for (i=0, len=items.length; i < len; i++) {

Variable declaration hoisting means defining a variable anywhere in a function is the same as declaring it at the top of the function. Therefore, a popular style is to have all variables declared at the top of a function instead of scattered throughout. In short, you end up writing code similar to the manner in which the JavaScript engine will interpret it.

My recommendation is to have your local variables defined as the first statements in a function. This approach is recommended in Crockford’s Code Conventions, the SproutCore Style Guide, and the Dojo Style Guide:

function doSomethingWithItems(items) {

    var i, len;
    var value = 10;
    var result = value + 10;

    for (i=0, len=items.length; i < len; i++) {

Crockford goes on to recommend the use of a single var statement at the top of functions:

function doSomethingWithItems(items) {

    var i, len,
        value = 10,
        result = value + 10;

    for (i=0, len=items.length; i < len; i++) {

The Dojo Style Guide allows combining var statements only when the variables are related to one another.

My personal preference is to combine all var statements with one initialized variable per line. The equals signs should be aligned. For variables that aren’t initialized, they should appear last, as in the following example:

function doSomethingWithItems(items) {

    var value   = 10,
        result  = value + 10,

    for (i=0, len=items.length; i < len; i++) {

At a minimum, I recommend combining var statements, as doing so makes your code smaller and therefore faster to download.

Function Declarations

Function declarations, just like variable declarations, are hoisted by JavaScript engines. Therefore, it’s possible to use a function in code before it is declared:

// Bad

function doSomething() {
    alert("Hello world!");

This approach works because the JavaScript engine interprets the code as if it were the following:

// Bad
function doSomething() {
    alert("Hello world!");


Due to this behavior, it’s recommended that JavaScript functions always be declared before being used. This design appears in Crockford’s Code Conventions. Crockford also recommends that local functions be placed immediately after variable declarations within a containing function, as in:

function doSomethingWithItems(items) {

    var i, len,
        value = 10,
        result = value + 10;

    function doSomething(item) {
        // do something

    for (i=0, len=items.length; i < len; i++) {

Both JSLint and JSHint will warn when a function is used before it is declared.

Additionally, function declarations should never appear inside of block statements. For example, this code won’t behave as expected:

// Bad
if (condition) {
    function doSomething() {
} else {
    function doSomething() {

Exactly how this will work from browser to browser will vary. Most browsers automatically take the second declaration without evaluating condition; Firefox evaluates condition and uses the appropriate function declaration. This is a gray area in the ECMAScript specification and should thus be avoided. Function declarations should be used only outside of conditional statements. This pattern is explicitly forbidden in the Google JavaScript Style Guide.

Function Call Spacing

Almost universally, the recommended style for function calls is to have no space between the function name and the opening parenthesis, which is done to differentiate it from a block statement. For example:

// Good

// Bad: Looks like a block statement
doSomething (item);

// Block statement for comparison
while (item) {
    // do something

Crockford’s Code Conventions explicitly calls this out. The Dojo Style Guide, SproutCore Style Guide, and Google JavaScript Style Guide implicitly recommend this style through code examples.

The jQuery Core Style Guide further specifies that an extra space should be included after the opening parenthesis and before the closing parenthesis, such as:

// jQuery-style
doSomething( item );

The intent here is to make the arguments easier to read. The jQuery Core Style Guide also lists some exceptions to this style, specifically relating to functions that are passed a single argument that is an object literal, array literal, function expression, or string. So the following examples are all still considered valid:

// jQuery exceptions
doSomething(function() {});
doSomething({ item: item });
doSomething([ item ]);

Generally speaking, styles with more than one exception are not good, because they can be confusing to developers.

Immediate Function Invocation

JavaScript allows you to declare anonymous functions—functions without proper names—and assign those functions to variables or properties. For example:

var doSomething = function() {
    // function body

Such anonymous functions can also be immediately invoked to return a value to the variable by including parentheses at the very end:

// Bad
var value = function() {

    // function body

    return {
        message: "Hi"

In the previous example, value ends up being assigned an object, because the function is immediately invoked. The problem with this pattern is that it looks very similar to assigning an anonymous function to a variable. You don’t know that this isn’t the case until you get to the very last line and see the parentheses. This sort of confusion hinders the readability of your code.

To make it obvious that immediate function invocation is taking place, put parentheses around the function, as in this example:

// Good
var value = (function() {

    // function body

    return {
        message: "Hi"

This code now has a signal on the first line, the open paren, that the function is immediately invoked. Adding the parentheses doesn’t change the behavior of the code at all. Crockford’s Code Conventions recommends this pattern, and JSLint will warn when the parentheses are missing.

Strict Mode

ECMAScript 5 introduced strict mode, a way to alter how JavaScript is executed and parsed in the hopes of reducing errors. To put a script into strict mode, use the following pragma:

"use strict";

Although this looks like a string that isn’t assigned to a variable, ECMAScript 5 JavaScript engines treat this as a command to switch into strict mode. This pragma is valid both globally as well as locally, inside of a single function. However, it’s a common recommendation (though undocumented in any popular style guide) to avoid placing "use strict" in the global scope. The reason is that strict mode applies to all code in a single file, so if you’re concatenating 11 files and one of them has global strict mode enabled, all of the files are placed into strict mode. Because strict mode operates under slightly different rules than nonstrict mode, there’s a high likelihood of errors within the other files. For this reason, it’s best to avoid placing "use strict" in the global scope. Here are some examples:

// Bad - global strict mode
"use strict";

function doSomething() {
    // code

// Good
function doSomething() {
    "use strict";

    // code

If you want strict mode to apply to multiple functions without needing to write "use strict" multiple times, use immediate function invocation:

// Good
(function() {
    "use strict";

    function doSomething() {
        // code

    function doSomethingElse() {
        // code


In this example, doSomething() and doSomethingElse() both run in strict mode, because they are contained in an immediately invoked function with "use strict" specified.

Both JSLint and JSHint warn when "use strict" is found outside of a function. Both also expect all functions to have "use strict" specified by default; this can be turned off in both tools. I recommend using strict mode wherever possible to limit common mistakes.


Equality in JavaScript is tricky due to type coercion. Type coercion causes variables of a specific type to be converted automatically into a different type for a particular operation to succeed, which can lead to some unexpected results.

One of the main areas in which type coercion occurs is with the use of equality operators, == and !=. These two operators cause type coercion when the two values being compared are not the same data type (when they are the same data type, no coercion occurs). There are many instances in which code may not be doing what you expect.

If you compare a number to a string, the string is first converted to a number, and then the comparison happens. Some examples:

// The number 5 and string 5
console.log(5 == "5");          // true

// The number 25 and hexadecimal string 25
console.log(25 == "0x19");      // true

When performing type coercion, the string is converted to a number as if using the Number() casting function. Because Number() understands hexadecimal format, it will convert a string that looks like a hexadecimal number into the decimal equivalent before the comparison occurs.

If a boolean value is compared to a number, then the boolean is converted to a number before comparison. A false value becomes 0 and true becomes 1. For example:

// The number 1 and true
console.log(1 == true);     // true

// The number 0 and false
console.log(0 == false);    // true

// The number 2 and true
console.log(2 == true);     // false

If one of the values is an object and the other is not, then the object’s valueOf() method is called to get a primitive value to compare against. If valueOf() is not defined, then toString() is called instead. After that point, the comparison continues following the previously discussed rules about mixed type comparisons. For example:

var object = {
    toString: function() {
        return "0x19";

console.log(object == 25);      // true

The object is deemed to be equal to the number 25 because its toString() method returned the hexadecimal string "0x19", which was then converted to a number before being compared to 25.

The last instance of type coercion occurs between null and undefined. These two special values are deemed to be equivalent simply by the letter of the ECMAScript standard:

console.log(null == undefined);     // true

Because of type coercion, avoiding == and != at all is recommended; instead, use === and !==. These operators perform comparison without type coercion. So if two values don’t have the same data type, they are automatically considered to be unequal, which allows your comparison statements to always perform the comparison in a way that is more consistent. Consider the differences between == and === in a few cases:

// The number 5 and string 5
console.log(5 == "5");          // true
console.log(5 === "5");         // false

// The number 25 and hexadecimal string 25
console.log(25 == "0x19");      // true
console.log(25 === "0x19");     // false

// The number 1 and true
console.log(1 == true);         // true
console.log(1 === true);        // false

// The number 0 and false
console.log(0 == false);        // true
console.log(0 === false);       // false

// The number 2 and true
console.log(2 == true);         // false    
console.log(2 === true);        // false

var object = {
    toString: function() {
        return "0x19";

// An object and 25
console.log(object == 25);      // true
console.log(object === 25);     // false

// Null and undefined
console.log(null == undefined); // true
console.log(null === undefined);// false

Use of === and !== is recommended by Crockford’s Code Conventions, the jQuery Core Style Guide, and the SproutCore Style Guide. Crockford’s guide recommends usage all the time, but specifically for comparing against false values (those values that are coerced to false, such as 0, the empty string, null, and undefined). The jQuery Core Style Guide allows the use of == for comparison against null when the intent is to test for both null and undefined. I recommend using === and !== all the time without exception.

JSLint warns about all uses of == and != by default. JSHint warns about using == and != when comparing to a false value by default. You can enable warnings for all uses of == and != by adding the eqeqeq option.


The eval() function takes a string of JavaScript code and executes it. This function allows developers to download additional JavaScript code, or to generate JavaScript code on the fly, and then execute it. For example:


var count = 10;
var number = eval("5 + count");
console.log(count);     // 15

The eval() function isn’t the only way to execute a JavaScript string from within JavaScript. The same can be done using the Function constructor as well as setTimeout() and setInterval(). Here are some examples:

var myfunc = new Function("alert('Hi!')");

setTimeout("'red'", 50);

setInterval("document.title = 'It is now '" + (new Date()), 1000);

All of these are considered bad practice by most of the JavaScript community. Although eval() may be used from time to time in JavaScript libraries (mostly in relation to JSON), the other three uses are rarely, if ever, used. A good general guideline is to never use Function and to use eval() only if no other options are present. Both setTimeout() and setInterval() can be used but should use function instead of strings:

setTimeout(function() {'red';
}, 50);

setInterval(function() {
    document.title = 'It is now ' + (new Date());
}, 1000);

Crockford’s Code Conventions forbids the use of eval() and Function, as well as setTimeout() and setInterval() when used with strings. The jQuery Core Style Guide forbids the use of eval() except for a JSON parsing fallback used in one place. The Google JavaScript Style Guide allows the use of eval() only for converting Ajax responses into JavaScript values.

Both JSLint and JSHint warn about the use of eval(), Function, setTimeout(), and setInterval() by default.


ECMAScript 5 strict mode puts severe restrictions on eval(), preventing it from creating new variables or functions in the enclosing scope. This restriction helps close some of the security holes innate in eval(). However, avoiding eval() is still recommended unless there is absolutely no other way to accomplish the task.

Primitive Wrapper Types

A little-known and often misunderstood aspect of JavaScript is the language’s reliance on primitive wrapper types. There are three primitive wrapper types: String, Boolean, and Number. Each of these types exists as a constructor in the global scope and each represents the object form of its respective primitive type. The main use of primitive wrapper types is to make primitive values act like objects, for instance:

var name = "Nicholas";

Even though name is a string, which is a primitive type and therefore not an object, you’re still able to use methods such as toUpperCase() as if the string were an object. This usage is made possible because the JavaScript engine creates a new instance of the String type behind the scenes for just that statement. Afterward, it’s destroyed, and another is created when it is needed. You can test out this behavior by trying to add a property to a string:

var name = "Nicholas"; = true;
console.log(;   // undefined

The author property has vanished after the second line. That’s because the temporary String object representing the string was destroyed after line 2 executed, and a new String object was created for line 3. It’s possible to create these objects yourself as well:

// Bad
var name = new String("Nicholas");
var author = new Boolean(true);
var count = new Number(10);

Although it’s possible to use these primitive wrapper types, I strongly recommend avoiding them. Developers tend to get confused as to whether they’re dealing with an object or a primitive, and bugs occur. There isn’t any reason to create these objects yourself.

The Google JavaScript Style Guide forbids the use of primitive wrapper types. Both JSLint and JSHint will warn if you try to use String, Number, or Boolean to create new objects.

The best content for your career. Discover unlimited learning on demand for around $1/day.