Exceptional Exception Handling in JavaScript

Share this article

Anything that can go wrong, will go wrong.  Murphy’s law is even applicable to software development.  For non-trivial programs, it’s not a matter of if, but when something will go wrong.  Standards non-compliance, unsupported features, and browser quirks are just a few sources of potential problems facing web developers.  Given all of the things that can go wrong, JavaScript has a surprisingly simple way of dealing with errors ― it just gives up and fails silently.  At least, that’s the behavior seen by the user.  In reality, there is a lot going on under the hood.

When a JavaScript statement generates an error, it is said to throw an exception.  Instead of proceeding to the next statement, the JavaScript interpreter checks for exception handling code.  If there is no exception handler, then the program returns from whatever function threw the exception.  This is repeated for each function on the call stack until an exception handler is found or until the top level function is reached, causing the program to terminate.

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 for exception handling.  Each of the error types is described in detail below.

Error

The “Error” type is used to represent generic exceptions.  This type of exception is most often used for implementing user defined exceptions.  The topic of creating user defined exceptions will be revisited later in this article.  “Error” objects are instantiated by calling their constructor as shown in the following example.

var error = new Error("error message");

“Error” objects contain two properties, “name” and “message”.  The “name” property specifies the type of exception (in this case “Error”).  The “message” property provides a more detailed description of the exception.  The “message” gets its value from the string passed to the exception’s constructor.  The remaining exception types represent more specific types of errors, but they are all used in the same fashion as the generic “Error” type.

RangeError

“RangeError” exceptions are generated by numbers that fall outside of a specified range.  For example, JavaScript numbers have a toFixed() method which takes a “digits” argument representing the number of digits to appear after a decimal point.  This argument is expected to be between 0 and 20 (although some browsers support a wider range).  If the value of “digits” is outside of this range, then a “RangeError” is thrown.  This scenario is shown in the following example.

var pi = 3.14159;

pi.toFixed(100000);  // RangeError

ReferenceError

A “ReferenceError” exception is thrown when a non-existent variable is accessed.  These exceptions commonly occur when an existing variable name is misspelled.  In the example below, a “ReferenceError” occurs when “bar” is accessed.  Note that this example assumes that “bar” does not exist in any active scope when the increment operation is attempted.

function foo() {
  bar++;  // ReferenceError
}

SyntaxError

A “SyntaxError” is thrown when the rules of the JavaScript language are broken.  Developers who are familiar with languages such as C and Java are used to encountering syntax errors during the compilation process.  However, because JavaScript is an interpreted language, syntax errors are not identified until the code is executed.  Syntax errors are unique as they are the only type of exception that cannot be recovered from.  The following example generates a syntax error because the “if” statement is missing a closing curly brace.

if (foo) {  // SyntaxError
  // the closing curly brace is missing

TypeError

A “TypeError” exception occurs when a value is not of the expected type.  Attempting to call a non-existent object method is a common cause of this type of exception.  The following example creates an empty object named “foo” and then attempts to invoke its bar() method.  Since bar() is not defined, a “TypeError” is thrown upon the attempted invocation.

var foo = {};

foo.bar(); // TypeError

URIError

A “URIError” exception is thrown by methods such as encodeURI() and decodeURI() when they encounter a malformed URI.  The following example generates a “URIError” while attempting to decode the string “%”.  The “%” character represents the beginning of a URI escape sequence.  Since nothing follows the “%” in this example, the string is an invalid escape sequence, and therefore a malformed URI component.

decodeURIComponent("%"); // URIError

EvalError

“EvalError” exceptions are thrown when the eval() function is used improperly.  These exceptions are not used in the most recent version of the EcmaScript standard.  However, they are still supported in order to maintain backwards compatibility with older versions of the standard.

Handling Exceptions

Now that we know what exceptions are, it’s time to learn how to stop them from crashing our programs.  JavaScript handles exceptions via the “try…catch…finally” statement.  A generic example statement is shown below.

try {
  // attempt to execute this code
} catch (exception) {
  // this code handles exceptions
} finally {
  // this code always gets executed
}

The first part of a “try…catch…finally” statement is the “try” clause.  The “try” clause is mandatory, and is used to delimit a block of code that the programmer suspects could generate an exception.  The “try” clause must be followed by one or both of the “catch” and “finally” clauses.

The “catch” Clause

The second part of “try…catch…finally” is the “catch” clause.  The “catch” clause is a block of code that is only executed if an exception occurs in the “try” clause.  Although the “catch” clause is optional, it isn’t possible to truly handle an exception without one.  This is because the “catch” clause stops the exception from propagating through the call stack, allowing the program to recover.  If an exception occurs within the “try” block, then control is immediately passed to the “catch” clause.  The exception that occurred is also passed to the “catch” block for processing.  The following example shows how a “catch” clause is used 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
}

Complex applications can generate a variety of exceptions.  In such cases, the “instanceof” operator can be used to differentiate 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 separately from 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
  }
}

The “finally” Clause

The last component of the “try…catch…finally” statement is the optional “finally” clause.  The “finally” clause is a block of code that is executed after the “try” and “catch” clauses, regardless of any errors.  The “finally” clause is useful for including clean up code (closing files, etc.) that needs to be executed no matter what.  Note that the “finally” clause is even executed if an exception occurs that is not caught.  In such a scenario, the “finally” clause is executed and then the thrown exception proceeds normally.

One interesting note about the “finally” clause is that it will be executed even if the “try” or “catch” clause executes a “return” statement.  For example, the following function returns false because the “finally” clause is the last thing to execute.

function foo() {
  try {
    return true;
  } finally {
    return false;
  }
}

Throwing Exceptions

JavaScript allows programmers to throw their own exceptions via the appropriately named “throw” statement.  This concept can be somewhat confusing to inexperienced developers.  After all, developers strive to write code that is free of errors, yet the “throw” statement intentionally introduces them.  However, intentionally throwing exceptions can actually lead to code that is easier to debug and maintain.  For example, by creating meaningful error messages it becomes easier to identify and resolve problems.

Several examples of the “throw” statement are shown below.  There is no restriction on the type of data that can be thrown as an exception.  There is also no limit on the number of times that the same data can be caught and thrown.  In other words, an exception 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");

While the “throw” statement can be used with any data type, there are certain benefits to using the built-in exception types.  Firefox, for example, gives special treatment to those objects by adding debugging information such as the filename and line number where the exception occurred.

As an example scenario, assume that a division operation occurs somewhere in your application.  Division can be a nuisance because of the possibility of division by zero.  In JavaScript, such an operation results in “NaN”.  This can lead to confusing results that are tricky to debug.  Things would be much simpler if the application complained loudly about the division by zero.  The following “if” statement accomplishes this for us by throwing an exception.

if (denominator === 0)
  throw new Error("Attempted division by zero!");

Of course, it might be more appropriate to use a “RangeError” as shown below.

if (denominator === 0)
  throw new RangeError("Attempted division by zero!");

Custom Exception Objects

We’ve just learned how to generate customized error messages using the built-in exception types.  However, another approach is to create new exception types by extending the existing “Error” type.  Because the new type inherits from “Error”, it can be used like the other built-in exception types.  While the topic of inheritance in JavaScript is beyond the scope of this article, a simple technique is covered here.

The following example returns to the problem of dealing with division by zero.  Instead of using an “Error” or “RangeError” object as we did earlier, we are going to create our own type of exception.  In this example, we are creating the “DivisionByZeroError” exception type.  The function in the example acts as the constructor for our new type.  The constructor takes care of assigning the “name” and “message” properties.  The final 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();
DivisionByZeroError.prototype.constructor = DivisionByZeroError;

Things to Remember

  • The “try…catch…finally” statement is used to handle exceptions.
  • The “try” clause identifies code that could potentially generate exceptions.
  • The “catch” clause is only executed when an exception occurs.
  • The “finally” clause is always executed, no matter what.
  • The “throw” statement is used to generate exceptions.
  • Custom exception objects should inherit from the existing “Error” types.

Image via Fotolia

Frequently Asked Questions on Exception Handling in JavaScript

What is the difference between a syntax error and a runtime error in JavaScript?

Syntax errors, also known as parsing errors, occur at compile time in traditional programming languages and at interpret time in JavaScript. They occur when the code is syntactically incorrect and cannot be parsed. For example, if you forget a closing bracket or use an illegal character, a syntax error will occur.

On the other hand, runtime errors occur during program execution, after successful compilation. These errors typically happen due to illegal operations performed by the code. For instance, trying to access an undefined variable or calling a non-existent function will result in a runtime error.

How can I handle exceptions in JavaScript?

JavaScript provides a try…catch statement for exception handling. The try block contains the code that may potentially throw an exception, while the catch block contains the code that will execute if an exception is thrown in the try block. Here’s a simple example:

try {
// code that may throw an exception
} catch (error) {
// code to handle the exception
}

What is the purpose of the ‘finally’ block in JavaScript?

The ‘finally’ block in JavaScript is used to execute code after a try and catch block regardless of the result. This means that the code within the ‘finally’ block will execute whether an exception was thrown or not. It’s often used for cleaning up after your code, such as closing files or clearing resources.

Can I throw my own exceptions in JavaScript?

Yes, JavaScript allows you to throw your own exceptions using the ‘throw’ statement. You can throw exceptions of any type, including strings, numbers, booleans or objects. Once an exception is thrown, the normal flow of the program is halted and control is passed to the nearest exception handler.

How can I create a custom error type in JavaScript?

JavaScript allows you to create custom error types by extending the built-in Error object. This can be useful when you want to throw specific types of errors in your code. Here’s an example:

class CustomError extends Error {
constructor(message) {
super(message);
this.name = 'CustomError';
}
}

throw new CustomError('This is a custom error');

What is error propagation in JavaScript?

Error propagation refers to the process by which an error is passed up the call stack from where it was thrown to the nearest exception handler. If an error is not caught in the function where it was thrown, it will propagate up to the calling function, and so on, until it is either caught or it reaches the global scope, at which point the program will terminate.

How can I log errors in JavaScript?

JavaScript provides the console.error() method for logging errors. This method works just like console.log(), but it also includes the stack trace in the log and highlights the message as an error in the console. Here’s an example:

try {
// code that may throw an error
} catch (error) {
console.error(error);
}

What is the difference between ‘null’ and ‘undefined’ in JavaScript?

In JavaScript, ‘null’ and ‘undefined’ both represent the absence of a value. However, they are used in slightly different contexts. ‘Undefined’ means a variable has been declared but has not yet been assigned a value. On the other hand, ‘null’ is an assignment value that represents no value or no object.

How can I handle asynchronous errors in JavaScript?

Asynchronous errors can be handled using promises or async/await in JavaScript. Promises have a ‘catch’ method that is called when the promise is rejected. With async/await, you can use a try/catch block just like with synchronous code. Here’s an example with async/await:

async function asyncFunction() {
try {
let result = await someAsyncOperation();
} catch (error) {
console.error(error);
}
}

What is a TypeError in JavaScript?

A TypeError is thrown in JavaScript when an operation could not be performed, typically when a value is not of the expected type. For example, trying to call a non-function or accessing properties on undefined will result in a TypeError.

Colin IhrigColin Ihrig
View Author

Colin Ihrig is a software engineer working primarily with Node.js. Colin is the author of Pro Node.js for Developers, and co-author of Full Stack JavaScript Development with MEAN. Colin is a member of the Node.js Technical Steering Committee, and a hapi core team member. Colin received his Bachelor of Science in Engineering, and Master of Science in Computer Engineering from the University of Pittsburgh in 2005 and 2008, respectively.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week
Loading form