Exceptional Exception Handling in JavaScript
All Error objects
When an exception occurs, an object representing the error is created and thrown. The JavaScript language defines seven types of built-in error objects. These error types are the foundation of exception handling. Each error type is described in detail below.
Error Common exceptions
The “Error” type is used to represent ordinary exceptions. This type of exception is most often used to implement user-defined exceptions. The topic of creating user-defined exceptions is covered in more detail later in this article. The “Error” object is instantiated by calling its constructor, as shown in the following example:
var error = new Error("error message");
Copy the code
The “Error” object contains two properties, “Name” and “message”.
- The “Name” attribute specifies the type of exception (in this case, “Error”).
- The “message” property provides a more detailed description of the exception. “Message” gets its value from the string passed to the exception constructor.
The remaining exception types represent more specific Error types, but they are all used in the same way as the generic “Error” type.
RangeError Indicates the number error
The “RangeError” exception is generated by numbers outside the specified range. For example, JavaScript numbers have a toFixed() method that takes a “digits” argument, representing the number of digits that appear after the decimal point. This parameter is expected to be between 0 and 20 (although some browsers support a larger range). If the value of “digits” is outside this range, a “RangeError” is raised. This scenario is shown in the example below.
var pi = 3.14159;
pi.toFixed(100000); // RangeError
Copy the code
3. ReferenceError variable does not exist
When a variable that does not exist is accessed, a “ReferenceError” exception is thrown. These exceptions usually occur when the name of an existing variable is misspelled. In the example below, a “ReferenceError” appears when “bar” is accessed. Note that this example assumes that “bar” does not exist in any active scope when an increment operation is attempted.
function foo() {
bar++; // ReferenceError
}
Copy the code
4, SyntaxError SyntaxError exceptions
When a JavaScript language rule is broken, a “SyntaxError” is thrown. Developers familiar with languages such as C and Java often encounter syntax errors during compilation. However, because JavaScript is an interpreted language, syntax errors are not recognized until the code is executed. Syntax errors are unique because they are the only type of exception that cannot be recovered. The following example generates a syntax error because the “if” statement is missing the closing curly brace.
if (foo) { // SyntaxError
// the closing curly brace is missing
Copy the code
5, TypeError
A “TypeError” exception occurs when a value is not of the expected type. Trying to call a nonexistent object method is a common cause of this type of exception. The following example creates an empty object named “foo” and then attempts to call its bar() method. Because bar() is undefined, “TypeError” is thrown when the call is attempted.
var foo = {};
foo.bar(); // TypeError
Copy the code
6, URIError
Methods such as encodeURI() and decodeURI() throw a “URIError” exception when they encounter a malformed URI. The following example generates a “URIError” when attempting to decode the string “%”. The character “%” indicates the beginning of the URI escape sequence. Since there is nothing after “%” in this case, the string is an invalid escape sequence and therefore a malformed URI component.
decodeURIComponent("%"); // URIError
Copy the code
7, EvalError
When the eval() function is used improperly, an “EvalError” exception is raised. These exceptions are not used in the latest version of the EcmaScript standard. However, they are still supported to maintain backward compatibility with older versions of the standard.
Second, handling exceptions
Use try… The catch… Finally to handle exceptions
try {
// attempt to execute this code
} catch (exception) {
// this code handles exceptions
} finally {
// this code always gets executed
}
Copy the code
“Try… The catch… The first part of the finally statement is the “try” clause. The “try” clause is mandatory and is used to separate blocks of code that programmers suspect might generate an exception. A try clause must be followed by one or two catch and finally clauses.
Catch clause
“Try… The catch… The second part of finally is the “catch” clause. The catch clause is a block of code that is executed only when an exception occurs in the “try” clause. Although the “catch” clause is optional, it is impossible to truly handle exceptions without it. This is because the “catch” clause prevents exceptions from propagating through the call stack, allowing the program to recover. If an exception occurs in the “try” block, control is immediately passed to the “catch” clause. Exceptions that occur are also passed to a “catch” block for processing. The following example shows how to use the “catch” clause to handle a “ReferenceError”. Note that the “ReferenceError” object is available in the “catch” clause via the “exception” variable.
try {
foo++; // ReferenceError
} catch (exception) {
var message = exception.message;
// handle the exception
}
Copy the code
Complex applications can generate a wide variety of exceptions. In this case, you can use the “instanceof” operator to distinguish between the various types of exceptions. In the following example, assume that the “try” clause can generate several types of exceptions. The corresponding “catch” clause uses “instanceof” to handle “TypeError” and “ReferenceError” exceptions alone, rather than all other types of errors.
try {
// assume an exception occurs
} catch (exception) {
if (exception instanceof TypeError) {
// Handle TypeError exceptions
} else if (exception instanceof ReferenceError) {
// Handle ReferenceError exceptions
} else {
// Handle all other types of exceptions}}Copy the code
The finally clause
The “finally” clause, which is executed after the “try” and “catch” clauses regardless of errors, is useful for including cleanup code (closing files, etc.) that needs to be executed anyway.
Note that if an uncaught exception occurs, the “finally” clause is actually still executed. In this case, the exception thrown after the “finally” clause continues normally.
An interesting note about the “finally” clause is that it is executed even if the “try” or “catch” clause executes a “return” statement. For example, the following function returns false because the “finally” clause is executed last.
function foo() {
try {
return true;
} finally {
return false; }}Copy the code
Throw an exception
JavaScript allows programmers to throw their own exceptions through appropriately named “throw” statements. This concept can be confusing to inexperienced developers. After all, developers strive to write error-free code, and “throw” statements intentionally introduce errors. However, intentionally throwing exceptions actually makes the code easier to debug and maintain. For example, problems can be more easily identified and resolved by creating meaningful error messages.
Several examples of “throw” statements are shown below. There are no restrictions on the types of data that can be thrown as exceptions. There is no limit to how many times the same data can be caught and thrown. In other words, exceptions can be thrown, caught, and then thrown again.
throw true;
throw 5;
throw "error message";
throw null;
throw undefined;
throw {};
throw new SyntaxError("useful error message");
Copy the code
Although the “throw” statement can be used for any data type, there are some benefits to using built-in exception types. For example, Firefox makes these objects special by adding debugging information such as the file name and line number of the exception that occurred.
As an example scenario, assume that a division operation occurs somewhere in the application. Division is tricky because it can be divided by zero. In JavaScript, such an operation would result in “NaN”. This can lead to messy results that are difficult to debug. Things are much simpler if the application complains loudly about being divided by zero. The following “if” statement does this for us by throwing an exception.
if (denominator === 0)
throw new Error("Attempted division by zero!");
Copy the code
Of course, it might be more appropriate to use “RangeError” as shown below.
if (denominator === 0)
throw new RangeError("Attempted division by zero!");
Copy the code
4. Customize exception objects
We just learned how to generate custom error messages using built-in exception types. Another approach, however, is to create new exception types by extending existing “Error” types. Because the new type inherits from “Error”, it can be used just like any other built-in exception type. While the topic of inheritance in JavaScript is beyond the scope of this article, this article introduces a simple technique.
The following example returns to dealing with division by zero. Instead of using an “Error” or “RangeError” object as we did earlier, we will create our own exception type. In this case, we create the exception type “DivisionByZeroError.” The function in the example acts as a constructor for the new type. The constructor is responsible for assigning the “name” and “message” properties. The last two lines of the example cause the new type to inherit from the “Error” object.
function DivisionByZeroError(message) {
this.name = "DivisionByZeroError";
this.message = (message || "");
}
DivisionByZeroError.prototype = new Error(a); DivisionByZeroError.prototype.constructor = DivisionByZeroError;Copy the code
Things to keep in mind:
- “Try… The catch… The finally “statement is used to handle exceptions.
- The “try” clause identifies code that may raise an exception.
- The “catch” clause is only executed when an exception occurs.
- No matter what, the “finally” clause is always executed.
- The “throw” statement is used to generate exceptions.
- Custom exception objects should inherit from existing “Error” types.