In the ECMAScript 6 shadows: Native errors

ECMAScript 6 is coming up on the horizon, with JavaScript engines starting to implement certain features. There are some parts of the specification that have received a lot of attention: WeakMap, classes, generators, modules, and more. These represent large changes and additions over the ECMAScript 5 specification. However, there are certain smaller parts of the specification that are easy to miss yet equally important for the future of JavaScript. They are so small that barely anyone talks about them and you might not even know they are there unless you spend some time reading the actual specification. One such feature is native errors.


Errors today

To understand why native errors are so exciting, you first have to understand how errors work today. All errors thrown by the JavaScript engine inherit from `Error`, which is to say that all errors have `Error.prototype` in their prototype chain. The list of errors from ECMAScript 5 are:



Error – base type for all errors. Never actually thrown by the engine.
EvalError – thrown when an error occurs during execution of code via eval()
RangeError – thrown when a number is outside the bounds of its range. For example, trying to create an array with -20 items (new Array(-20)). These occur rarely during normal execution.
ReferenceError – thrown when an object is expected but not available, for instance, trying to call a method on a null reference.
SyntaxError – thrown when the code passed into eval() has a syntax error.
TypeError – thrown when a variable is of an unexpected type. For example, new 10 or "prop" in true.
URIError – thrown when an incorrectly formatted URI string is passed into encodeURI, encodeURIComponent, decodeURI, or decodeURIComponent.

You can also choose to throw these errors using their constructors, however, most developers throw their own custom errors. The uninitiated tend to start out by just using generic `Error` objects, such as:


throw new Error("This is my custom error message.");

The problem them becomes distinguishing between errors thrown by the JavaScript engine and errors thrown by developers. This is a very important aspect of a good error handling strategy in applications. The traditional way to do this is to create your own base error type, which inherits from `Error`, and then have all of your other error types inherit from that. For example:


function MyError(message) {
this.message = message;
}

MyError.prototype = Object.create(Error.prototype, {
name: { value: "MyError" }
});

function ThatNameIsStupidError(message) {
this.message = message;
}

ThatNameIsStupidError.prototype = Object.create(MyError.prototype, {
name: { value: "ThatNameIsStupidError" }
});


function YouDidSomethingDumbError(message) {
this.message = message;
}

YouDidSomethingDumbError.prototype = Object.create(MyError.prototype, {
name: { value: "YouDidSomethingDumbError" }
});

So the custom errors to use are ThatNameIsStupidError and YouDidSomethingDumbError. Both inherit from MyError and the intention is that any developer-defined errors will inherit from that common base type, allowing you to do things like this:


try {
doSomethingThatMightThrowAnError();
} catch (error) {
if (error instanceof MyError) {
// handle developer-defined error
} else {
// handle native error
}
}

In this code, you check to see if the thrown error is an instance of MyError, knowing that all developer-defined errors inherit from that common type. It’s pretty straightforward to set this up, though it does require that extra base type to determine from where the error originated.


Enter native errors

ECMAScript 6 defines a new type of error called NativeError. Section 19.5.6[1] describes this object type:


When an ECMAScript implementation detects a runtime error, it throws a new instance of one of the NativeError objects defined in 19.5.5. Each of these objects has the structure described below, differing only in the name used as the constructor name instead of NativeError, in the name property of the prototype object, and in the implementation-defined message property of the prototype object.



Looking back at section 19.5.5 (mentioned in the first sentence), you’ll see that the NativeError objects are:



EvalError
RangeError
ReferenceError
SyntaxError
TypeError
URIError

That means all of the runtime errors thrown by the JavaScript engine now inherit from NativeError instead of inheriting from Error (NativeError inherits from Error, so all existing code using instanceof Error will still work). That means you will one day be able to tell the difference between your errors and the JavaScript engine’s errors using code like this:


try {
doSomethingThatMightThrowAnError();
} catch (error) {
if (error instanceof NativeError) {
// handle native error
} else {
// handle developer-defined error
}
}

So instead of needing to create your own custom base error type (like MyError), any developer-defined error will be easily identifiable because it does not inherit from NativeError. That means you get to write less code to be able to make this distinction amongst errors.


Conclusion

Error handling isn’t a sexy topic, but one that becomes more important as your application grows larger. This subtle change in how errors work is both brilliant and effective at solving one of the significant pain points in JavaScript regarding handling errors. I’m very happy to see this change in ECMAScript 6 and look forward to seeing what other interesting features are lurking in the shadows of the specification.


References

ECMAScript 6: Native Error Object Structure



 •  0 comments  •  flag
Share on Twitter
Published on November 12, 2013 07:00
No comments have been added yet.


Nicholas C. Zakas's Blog

Nicholas C. Zakas
Nicholas C. Zakas isn't a Goodreads Author (yet), but they do have a blog, so here are some recent posts imported from their feed.
Follow Nicholas C. Zakas's blog with rss.