Custom exception handling in JavaScript

A co-worker showed me a neat trick today. Many of you have no doubt seen it, but I thought I would use it in a discussion about custom exception handling in JavaScript. I was buried in some Java Coding, so the topic was fresh.

The trick:

Math.max.apply(Math, array);

Awesome. It returns the highest value in the array.

What happens if a member of array can’t be parsed as a Number? It returns NaN. Which is great, but I wondered if I can wrap that in a try/catch block and catch NaN. Not exactly, but I can come close.

The try/catch/finally mechanism isn’t exactly new in JavaScript, but it is, In my opinion, under-utilized. Mostly, developers use it to protect code from errors. Consider:

var foo;

try {
    foo.undefinedFunction("bar");
} catch (ex) {
    console.log(ex);
}

alert("the error didn't stop me.");

In this example, the try catch is used instead of the traditional if (typeof foo.undefinedFunction !== “undefined”) .

But it is possible with JavaScript keyword throw to implement exception handling.

function getMaxValueOfArray(ar) /* throws illegal argument error, NaN error */ {
    var n;
    if (ar instanceof Array) {
        if (isNaN(n = Math.max.apply(Math, ar))) {
            throw new Error("NaN error. The first argument to getMax must be an array of Numbers");
        }

        return n;
    } else {
        throw new Error("illegal Argument. The first argument to getMax must be an instance of Array");
    }
}

var max;
try {
   max = getMaxValueOfArray('1');
} catch (ex) {
  alert(ex);
}

try {
    max = getMaxValueOfArray([1, 'foo', 2]);
} catch(ex) {
   alert(ex);
}

try {
     max = getMaxValueOfArray([1, 4, 2]);
} catch(ex) {
     alert(ex);
}

max;

We can extend the practice and add some more debugging information for FireFox with a custom function:


function CustomError(errorName, errorMessage) {
   var error = new Error();
   error.name = errorName;
   error.message = errorMessage;

// For FireFox at least, there is property called stack to hold the stack trace.
   if (typeof error.stack !== "undefined") {
      var errorstack = error.stack.split(/\s*(@|at)\s*/);
      errorstack = errorstack[errorstack.length - 1]
                   .replace(/^\s+|\s+$/g, '')
                   .split(/\:([0-9]+)/);
      error.fileName = errorstack[0];
      error.lineNumber = errorstack[1];
   }
   return error;
}

function getMaxValueOfArray(ar) /* throws illegal argument error, NaN error */ {
    var n;
    if (ar instanceof Array) {
        if (isNaN(n = Math.max.apply(Math, ar))) {
            throw new CustomError("NaN Error", "The first argument to getMax must be an array of Numbers");
        }

        return n;
    } else {
        throw new CustomError("illegalArgument Error", "The first argument to getMax must be an instance of Array");
    }
}

var max;
try {
   max = getMaxValueOfArray('1');
} catch (ex) {
   ex.stack ? alert(ex.stack) : alert(ex);
}

try {
    max = getMaxValueOfArray([1, 'foo', 2]);
} catch(ex) {
    ex.stack ? alert(ex.stack) : alert(ex);
}

try {
     max = getMaxValueOfArray([1, 4, 2]);
} catch(ex) {
     ex.stack ? alert(ex.stack) : alert(ex);
}

Further reading: http://www.sitepoint.com/throwing-better-errors-using-stack/

Leave a Reply

Your email address will not be published. Required fields are marked *