【问题标题】:Inheriting from the Error object - where is the message property?从 Error 对象继承 - message 属性在哪里?
【发布时间】:2012-02-06 20:08:37
【问题描述】:

我在 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; // "" !

为什么new Error("message") 设置message 属性,而Error.call(this, msg); 不设置?当然,我可以在 MyError 构造函数中定义 this.message = msg,但我不太明白为什么一开始还没有设置它。

【问题讨论】:

    标签: javascript


    【解决方案1】:
    function MyError(msg) {
        var err = Error.call(this, msg);
        err.name = "MyError";
        return err;
    }
    

    Error 不会操纵this,它会创建一个返回的新错误对象。这就是为什么 Error("foo") 在没有 new 关键字的情况下也能正常工作的原因。

    请注意,这是特定于实现的,v8 (chrome & node.js) 的行为是这样的。

    另外MyError.prototype.__proto__ = Error.prototype; 是一种不好的做法。使用

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

    【讨论】:

    • 感谢您的回答,以及 Object.create 继承提示!
    • 这并不完全正确。不使用new 关键字只会导致Error 构造函数使用new 关键字递归调用自身,这反过来又会使堆栈跟踪在Error 构造函数内部开始,而不是在您自己的代码中你初始化了通话。因此,请始终使用 new 关键字。
    • -1 用于在没有解释的情况下宣布某事是一种不好的做法(据我所知,这可能是一件完全愚蠢的事情,但我不知道为什么阅读您的答案)以及推荐 Object.create 作为替代方案,但不承认它在 IE 8 及更低版本中不受支持。
    • -1 没有正确子类化并且 instanceof 不起作用。请参阅下面的答案以获得更完整的解决方案
    • “下面的答案”是相对的。 2014 年 6 月 27 日的下方是什么? :D
    【解决方案2】:

    A.就像,Raynos 说,没有设置 message 的原因是 Error 是一个返回新错误对象的函数,并且以任何方式操纵 this

    B.做到这一点的方法是在this上设置构造函数的应用结果,以及以通常复杂的javascripty方式设置原型:

    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 ...>
    

    目前这种方式的唯一问题(我已经迭代了一点)是

    • stackmessage 以外的属性不包含在 MyError 中,并且
    • stacktrace 有一个额外的行,这不是真正需要的。

    第一个问题可以通过使用此答案中的技巧迭代所有不可枚举的错误属性来解决:Is it possible to get the non-enumerable inherited property names of an object?,但这不受 ie

    更新

    我创建了一个继承库来执行此操作 ^https://github.com/fresheneesz/proto

    【讨论】:

    • 这是(可悲的)正确的做法。接受的答案不会将其设置为 MyError 类型,而只是创建类型 Error。所以自定义错误处理是不可能的。
    • 你也可以使用Object.create代替function x(){},然后是x.prototype = Error.prototype,然后是new x()
    • 我很困惑。如果Error 不操纵this,你为什么要使用Error.apply(this, arguments) 而不是Error.apply(null, arguments)
    • 为什么需要IntermediateInheritor
    • 而且我认为没有必要返回this
    【解决方案3】:

    您可以使用 Error.captureStackTrace 过滤掉堆栈跟踪中不需要的行。

    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 ...>
    

    【讨论】:

      【解决方案4】:

      在 Node.js 中,您可以像这样创建自定义错误:

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

      请参阅节点文档中的 captureStackTrace

      【讨论】:

        【解决方案5】:

        另一种方法是使新的错误实例成为this 的原型,这样您就不必知道要复制哪些属性,从而解决了 BT 在其答案末尾谈到的问题.

        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
        

        对于我的钱来说,它更整洁一些。 但是请注意Object.setPrototypeOf() (或object.__proto__ = 在支持它的非 ES6 兼容实现上) 可能非常慢,所以如果您在黄金路径上使用这些错误那么你可能不想这样做。

        【讨论】:

          【解决方案6】:

          我非常喜欢制作可重复使用的 .js 文件,我将这些文件放入我参与的几乎所有项目中。当我有时间它会变成一个模块。

          对于我的错误,我创建了一个 exceptions.js 文件并将其添加到我的文件中。

          这是该文件中的代码示例:

          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,
          };
          

          在我项目的其他文件中,我必须在文件顶部添加此 require 行。

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

          要使用这个错误,你只需要这个。

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

          完整方法实现示例

          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);
            });
          };
          

          【讨论】:

            【解决方案7】:

            在 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
                }
            }
            

            【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2013-03-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-09-06
            • 2011-08-02
            相关资源
            最近更新 更多