The Error
class includes debugging information, such as the error's call stack, as properties of its instances. JS interpreters know how to serialise Error
instances into an informative error message string, and their structure can also be consumed by debugging software - such as browser dev tools - to construct a more informative GUI representation of the error. This is why it's generally more useful to throw an instance of the Error
class rather than simply throwing, for example, a string describing the error, or a number representing an error code.
Using custom errors
It's particularly useful to make your own subclasses of Error
, which allow you to uniquely identify different types of error with a descriptive name and machine-readable...
- clues for debugging,
- information for better user-facing error messages, or
- information to help recover from the error.
Then, when you're handling errors, you can use the nice and clean instanceof
operator to check what kind of error occurred. e.g.:
class DangerousWaterCurrent extends Error {
constructor(waterSpeed){
super(`These waters are moving at ${waterSpeed} metres per second - too fast to cross!`) // Provide a `message` argument to the Error() constructor
this.waterSpeed = waterSpeed // This passes some context about why/how the error occurred back to whichever function is going to catch & handle it
}
}
// ...later...
try {
swimAcrossRiver(river)
} catch (thrownValue) {
if (thrownValue instanceof DangerousWaterCurrent) {
if (thrownValue.waterSpeed <= 3){
paddleKayak(river)
} else {
constructBridge(river)
}
} else {
throw thrownValue // "Re-throw" the error back up the execution chain, for someone else to handle
}
}
new Error()
vs Error()
There is a "convenient" shorthand way to make an instance of Error
: by calling Error(message)
, instead of new Error(message)
, the way you'd make an instance of a normal class. This is a deliberate exception, inserted by the language designers, to the rule. There are similar shorthands for other in-language classes, like Number()
and String()
. They also let you call these classes with ()
as if they were functions, not classes. JS doesn't allow normal classes to do this, even though they're all actually functions under the syntactical sugar of "classes". Try in a REPL:
> class E extends Error {}
> Error(); "a value"
"a value"
> E(); "a value"
Uncaught TypeError: Class constructor E cannot be invoked without 'new'
at <anonymous>:2:1
A broader opinion on design: Personally, I think this design decision was a mistake, as it adds more exceptions to the rules of JavaScript - which means more to learn for programmers, and more for language translators/interpreters to account for. Instead of C++/Java's new
keyword, simply calling a class as if it were a function (as in Number("abc123")
) should have the properties that the new
keyword currently has: the class's constructor
function should be executed. Inside that function, this
should be bound to the instance and then returned implicitly. The new
keyword could then be discarded from the language. This is how Python's syntax works, and it's simpler, more readable, and more convenient.
Error
s – Wilmercreates and initializes a new Error object when called as a function rather than as a constructor. Thus the function call Error(…) is equivalent to the object creation expression new Error(…) with the same arguments.
Spec in tc39.es/ecma262/#sec-error-constructor – Disc