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/