Inheriting from the Error object - where is the message property?
Asked Answered
E

7

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.

Exhilarative answered 10/1, 2012 at 11:52 Comment(0)
B
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

Biflagellate answered 30/7, 2013 at 0:49 Comment(14)
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.Shallop
You could also use Object.create instead function x(){}, then x.prototype = Error.prototype, then new x()Parados
PD: +1, this got me on the wayParados
I'm confused. If Error doesn't manipulate this, why are you doing Error.apply(this, arguments) instead of Error.apply(null, arguments)?Cleopatra
Its just the standard way to cleanly pass the entirety of the function-call context to another function (ie the this and the arguments). I could have done Error(arguments[0]), but A. I wasn't entirely sure there couldn't be other arguments, and B. I wasn't entirely sure that nothing is done with this - there may be things it does with it that don't involve modifying it. Probably not, but I wasn't 100% sure.Biflagellate
I'm curious how much of a performance hit is made from accessing the tmp.stack getter. I know it's expensive enough for at least V8 to make a getter...Ectoblast
Interesting point mike, I hadn't actually thought about it being a getter, but I've seen the (...) in the debugger so many times, I should have realized it. Creating a getter isn't expensive, but building the stack trace might be (otherwise why would they have used a getter, right?). The most expensive stuff (recording the information that would allow the creation of a stack trace) has already been done at this point tho, so the savings aren't huge. An easy solution would be to make this.stack into a getter as wellBiflagellate
why do you need the IntermediateInheritor?Hbeam
And also I don't think returning this is necessary.Hbeam
You need some way to add methods to the new Error type without modifying the original Error type. The intermediate inheritor is the prototype you would attach new methods to. You might be able to just instantiate an Error object and add methods to that object, then use it as the prototype of MyError. I don't remember the intricacies of this anymore tho, cause these days I just use proto to do javascript inheritnace.Biflagellate
I simplified and improved this approach a bit: jsbin.com/rolojuhuya/1/edit?js,consoleSass
regardless to the discussion, you might want to look at github.com/osher/error-stringifyEdgaredgard
Apparently the custom constructor isn't displayed in the console when new is omitted.Mcmasters
Are there any downsides to declaring the name and the stack property on the prototype? It seems like it would save memory when newing up lots of errors. Example here: jsbin.com/caboqij/edit?js,consoleGrandpapa
C
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 } 
});
Colorant answered 10/1, 2012 at 14:6 Comment(6)
Thanks for the answer, and for the Object.create inheritance tip!Exhilarative
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.Cory
I'm confused. If "Error doesn't manipulate this", why are you doing Error.call(this, msg) instead of, say, Error.call(null, msg)?Cleopatra
-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.Widthwise
-1 for not subclassing correctly and having instanceof not work. See answer bellow for a more complete solutionHenry
"answer below" is rather relative. What was below on June 27th in 2014? :DUnclinch
V
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

Virology answered 23/12, 2015 at 8:50 Comment(0)
P
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
    }
}
Perennial answered 22/9, 2018 at 22:32 Comment(1)
That was not possible back in 2012, when this question was asked. This is a similar question about ES6: #31090301Exhilarative
E
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 ...>
Errecart answered 17/7, 2015 at 13:44 Comment(0)
M
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.

Meerkat answered 7/1, 2017 at 22:12 Comment(0)
M
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);
  });
};
Moynahan answered 15/3, 2018 at 20:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.