36

I noticed a strange behavior while defining custom error objects in Javascript:

function MyError(msg) { Error.call(this, msg); this.name = "MyError"; } MyError.prototype.__proto__ = Error.prototype; var error = new Error("message"); error.message; // "message" var myError = new MyError("message"); myError instanceof Error; // true myError.message; // "" ! 

Why does new Error("message") set the message property, while Error.call(this, msg); does not? Sure, I can just define this.message = msg in the MyError constructor, but I don't quite understand why it is not already set in the first place.

7 Answers 7

41

A. Like, Raynos said, The reason message isn't being set is that Error is a function that returns a new Error object and does not manipulate this in any way.

B. The way to do this right is to set the result of the apply from the constructor on this, as well as setting the prototype in the usual complicated javascripty way:

function MyError() { var tmp = Error.apply(this, arguments) tmp.name = this.name = 'MyError' this.message = tmp.message // instead of this.stack = ..., a getter for more optimizy goodness Object.defineProperty(this, 'stack', { get: function () { return tmp.stack } }) return this } var IntermediateInheritor = function () {} IntermediateInheritor.prototype = Error.prototype MyError.prototype = new IntermediateInheritor() var myError = new MyError("message") console.log("The message is: '"+myError.message+"'") // The message is: 'message' console.log(myError instanceof Error) // true console.log(myError instanceof MyError) // true console.log(myError.toString()) // MyError: message console.log(myError.stack) // MyError: message \n // <stack trace ...> 

The only problems with this way of doing it at this point (i've iteratted it a bit) are that

  • properties other than stack and message aren't included in MyError, and
  • the stacktrace has an additional line that isn't really necessary.

The first problem could be fixed by iterating through all the non-enumerable properties of error using the trick in this answer: Is it possible to get the non-enumerable inherited property names of an object?, but this isn't supported by ie<9. The second problem could be solved by tearing out that line in the stack trace, but I'm not sure how to safely do that (maybe just removing the second line of e.stack.toString() ??).

Update

I created an inheritance library that does this ^ https://github.com/fresheneesz/proto

Sign up to request clarification or add additional context in comments.

14 Comments

That is (sadly) the correct way of doing it. The accepted answer does not set it to type of MyError but just creates a type Error. So custom error handling is not possible.
You could also use Object.create instead function x(){}, then x.prototype = Error.prototype, then new x()
I'm confused. If Error doesn't manipulate this, why are you doing Error.apply(this, arguments) instead of Error.apply(null, arguments)?
why do you need the IntermediateInheritor?
And also I don't think returning this is necessary.
|
16
function MyError(msg) { var err = Error.call(this, msg); err.name = "MyError"; return err; } 

Error doesn't manipulate this, it creates a new error object which is returned. That's why Error("foo") works aswell without the new keyword.

Note this is implementation specific, v8 (chrome & node.js) behave like this.

Also MyError.prototype.__proto__ = Error.prototype; is a bad practice. Use

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

6 Comments

Thanks for the answer, and for the Object.create inheritance tip!
This is not entirely true. Not using the new keyword only results in the Error constructor to recursively call itself with the new keyword, which in turn makes the stack-trace start inside the Error constructor instead of in place in your own code where you initialized the call. Hence, always use the new keyword.
-1 for declaring something to be a bad practice without explanation (it may well be an utterly stupid thing to do for all I know, but I have no idea why from reading your answer) and also for recommending Object.create as the alternative without acknowledging that it's unsupported in IE 8 and below.
-1 for not subclassing correctly and having instanceof not work. See answer bellow for a more complete solution
"answer below" is rather relative. What was below on June 27th in 2014? :D
|
11

In Node.js you can create a custom error like this:

var util = require('util'); function MyError(message) { this.message = message; Error.captureStackTrace(this, MyError); } util.inherits(MyError, Error); MyError.prototype.name = 'MyError'; 

See captureStackTrace in node docs

Comments

2

What's wrong with doing it this way in ES6?

class MyError extends Error { constructor(message) { super(message); // Maintains proper stack trace (only on V8) if (Error.captureStackTrace) { Error.captureStackTrace(this, MyError); } this.appcode= 123; // can add custom props } } 

1 Comment

That was not possible back in 2012, when this question was asked. This is a similar question about ES6: stackoverflow.com/questions/31089801/…
1

You can use Error.captureStackTrace for filtering out unneeded line in stack trace.

function MyError() { var tmp = Error.apply(this, arguments); tmp.name = this.name = 'MyError'; this.message = tmp.message; /*this.stack = */Object.defineProperty(this, 'stack', { // getter for more optimizy goodness get: function() { return tmp.stack; } }); Error.captureStackTrace(this, MyError); // showing stack trace up to instantiation of Error excluding it. return this; } var IntermediateInheritor = function() {}, IntermediateInheritor.prototype = Error.prototype; MyError.prototype = new IntermediateInheritor(); var myError = new MyError("message"); console.log("The message is: '"+myError.message+"'"); // The message is: 'message' console.log(myError instanceof Error); // true console.log(myError instanceof MyError); // true console.log(myError.toString()); // MyError: message console.log(myError.stack); // MyError: message \n // <stack trace ...> 

Comments

0

Another approach to this is to make the new error instance the prototype of this, and that way you don't have to know what properties to copy, which gets around the problems B T talked about at the end of their answer.

function MyError() { if (this === undefined) { throw TypeError("MyError must be called as a constructor"); } let newErr = Error.apply(undefined, arguments); Object.setPrototypeOf(newErr, MyError.prototype); Object.setPrototypeOf(this, newErr); } MyError.prototype = Object.create(Error.prototype); let me = new MyError("A terrible thing happened"); console.log(me instanceof MyError); // true console.log(me instanceof Error); // true console.log(me.message); // A terrible thing happened 

And for my money it's a bit neater. But note that Object.setPrototypeOf() (or object.__proto__ = on non ES6 compliant implementations that support it) can be very slow, so if you are using these errors on your golden paths then you may not want to do this.

Comments

0

I like a lot to make reusable .js files that I put in almost any project I participate. When i have time it will become a module.

For my errors i create a exceptions.js file and add it on my files.

Here is the example of the code inside this file:

const util = require('util'); /** * This exception should be used when some phat of code is not implemented. * @param {String} message Error message that will be used inside error. * @inheritDoc Error */ function NotImplementedException(message) { this.message = message; Error.captureStackTrace(this, NotImplementedException); } util.inherits(NotImplementedException, Error); NotImplementedException.prototype.name = 'NotImplementedException'; module.exports = { NotImplementedException, }; 

In the other files of my project i must have this require line on top of the file.

const Exceptions = require('./exceptions.js'); 

And to use this error you just need this.

const err = Exceptions.NotImplementedException(`Request token ${requestToken}: The "${operation}" from "${partner}" does not exist.`); 

Example of a full method implementation

const notImplemented = (requestToken, operation, partner) => { logger.warn(`Request token ${requestToken}: To "${operation}" received from "${partner}"`); return new Promise((resolve, reject) => { const err = Exceptions.NotImplementedException(`Request token ${requestToken}: The "${operation}" from "${partner}" does not exist.`); logger.error(err.message); return reject(err); }); }; 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.