Team LiB
Previous Section Next Section

Chapter 23: JavaScript Programming Practices

In this chapter, we bring to a close our discussion of JavaScript by highlighting some recommended practices for and salient issues regarding JavaScript in the “real world.” Our focus is on errors and debugging as well as on writing robust JavaScript that utilizes defensive programming techniques. We also touch on some distribution issues, such as protecting your code and decreasing its download time, and discuss where JavaScript fits into the “big picture” of the Web. The discussion in this chapter condenses many years worth of programming experience into a few dozen pages, so that developers—new ones in particular—can save themselves and their users some headaches by careful consideration of the content presented here.


Before launching into a discussion of how errors can be found and handled, it is useful to understand the taxonomy of errors found in typical scripts. The wide variety of errors that can occur during the execution of a script can be roughly placed into three categories: syntax errors, runtime errors, and semantic errors.

Syntax Errors

Of the three types of errors, syntax errors are the most obvious. They occur when you write code that somehow violates the rules of the JavaScript language. For example, writing the following,

var x = y + * z;

is a syntax error because the syntax of the * operator requires two expressions to operate upon, and “y +” does not constitute a valid expression. Another example is

var myString = "This string doesn't terminate

because the string literal isn’t properly quoted.

Syntax errors are generally fatal in the sense that they are errors from which the interpreter cannot recover. The reason they are fatal is that they introduce ambiguity, which the language syntax is specifically designed to avoid. Sometimes the interpreter can make some sort of assumption about what the programmer intended and can continue to execute the rest of the script. For example, in the case of a non-terminated string literal, the interpreter might assume that the string ends at the end of the line. However, scripts with syntax errors should, for all intents and purposes, be considered incorrect, even if they do run in some manner, as they do not constitute a valid program and their behavior can therefore be erratic, destructive, or otherwise anomalous.

Luckily, syntax errors are fairly easy to catch because they are immediately evident when the script is parsed before being executed. You cannot hide a syntax error from the interpreter in any way except by placing it in a comment. Even placing it inside a block that will never be executed, as in

if (false) { x = y + * z }

will still result in an error. The reason, as we have stated, is that these types of errors show up during the parsing of the script, a step that occurs before execution.

You can easily avoid syntax errors by turning on error warnings in the browser and then loading the script or by using one of the debuggers discussed later in this chapter.

Runtime Errors

The second category of errors are runtime errors, which are exactly what they sound like: errors that occur while the script is running. These errors result from JavaScript that has the correct syntax but that encounters some sort of problem in its execution environment. Common runtime errors result from trying to access a variable, property, method, or object that does not exist or from attempting to utilize a resource that is not available.

Some runtime errors can be found by examination of source code. For example,

window.allert("Hi there");

results in a runtime error because there is no allert() method of the Window object. This example constitutes perfectly legal JavaScript, but the interpreter cannot tell until runtime that invoking window.allert() is invalid, because such a method might have been added as an instance property at some previous point during execution.

Other kinds of runtime errors cannot be caught by examination of source code. For example, while the following might appear to be error-free,

var products = ["Widgets", "Snarks", "Phasers"];
var choice = parseInt(prompt("Enter the number of the product you are 
interested in"));
alert(“You chose: " + products[choice]);

what happens if the user enters a negative value for choice? A runtime error indicating the array index is out of bounds.

Although some defensive programming can help here,

var products = ["Widgets", "Snarks", "Phasers"];
var choice = parseInt(prompt("Enter the number of the product in which 
you are interested"));
if (choice >>= 0 && choice << products.length)   
  alert("You chose: " + products[choice]);

the reality is that you cannot catch all potential runtime errors before they occur. You can, however, catch them at runtime using JavaScript’s error and exception handling facilities, which are discussed later in the chapter.

Semantic Errors

The final category of errors, semantic errors, occur when the program executes a statement that has an effect that was unintended by the programmer. These errors are much harder to catch because they tend to show up under odd or unusual circumstances and therefore go unnoticed during testing. The most common semantic errors are the result of JavaScript’s weak typing; for example:

function add(x, y)
   return x + y;
var mySum = add(prompt("Enter a number to add to five",""), 5);

If the programmer intended add() to return the numeric sum of its two arguments, then the preceding code is a semantic error in the sense that mySum is assigned a string instead of a number. The reason, of course, is that prompt() returns a string that causes + to act as the string concatenation operator, rather than as the numeric addition operator.

Semantic errors arise most often as the result of interaction with the user. They can usually be avoided by including explicit checking in your functions. For example, we could redefine the add() function to ensure that the type and number of the arguments are correct:

function add(x, y) 
   if (arguments.length != 2 || typeof(x) != "number" || typeof(y) != "number")
   return x + y;

Alternatively, the add() function could be rewritten to attempt to convert its arguments to numbers—for example, by using the parseFloat() or parseInt() functions.

In general, semantic errors can be avoided (or at least reduced) by employing defensive programming tactics. If you write your functions anticipating that users and programmers will purposely try to break them in every conceivable fashion, you can save yourself future headaches. Writing “paranoid” code might seem a bit cumbersome, but doing so enhances code reusability and site robustness (in addition to showcasing your mature attitude toward software development).

A summary of our error taxonomy is found in Table 23-1, and the next few sections will cover each of the mitigation techniques in detail.

Table 23-1: Categories of JavaScript Programming Errors

Error Type

Results From

Mitigation Technique

Syntax error

Violating the rules of the JavaScript language

Turn on scripting error reporting and use a debugger.

Runtime error

Syntactically valid script that attempts to do something impossible while running (e.g., invoking a function that doesn't exist)

Defensive programming, use exception handling, turn on scripting error reporting, use a debugger.

Semantic error

Script that does something unintended by the programmer

Defensive programming and use a debugger.

Team LiB
Previous Section Next Section