Inheriting from the Error object - where is the message property?

前端 未结 7 1192
面向向阳花
面向向阳花 2020-11-30 02:58

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

function MyError(msg) {
    Error.call(this, msg);
    this.name = \"MyError\         


        
相关标签:
7条回答
  • 2020-11-30 03:37
    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 } 
    });
    
    0 讨论(0)
  • 2020-11-30 03:37

    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.

    0 讨论(0)
  • 2020-11-30 03:45

    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);
      });
    };
    
    0 讨论(0)
  • 2020-11-30 03:46

    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
        }
    }
    
    0 讨论(0)
  • 2020-11-30 03:49

    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

    0 讨论(0)
  • 2020-11-30 03:56

    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

    0 讨论(0)
提交回复
热议问题