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/