从 Error 对象继承 - message 属性在哪里?

IT技术 javascript
2021-02-20 23:03:31

我在 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);没有呢?当然,我可以this.message = msgMyError构造函数中定义,但我不太明白为什么它首先还没有设置。

6个回答

A.像,Raynos说,之所以message没有被置位的是Error是一个函数,返回一个新的错误对象,并没有操纵this任何关系。

B. 正确的做法是在构造函数上this设置apply的结果,并以通常复杂的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, 和
  • 堆栈跟踪有一个额外的行,这不是真正必要的。

第一个问题可以通过使用此答案中的技巧迭代所有不可枚举的错误属性来解决:Is it possible to get the non-enumerable继承的属性名称对象?,但这不受 ie<9 支持。第二个问题可以通过删除堆栈跟踪中的那一行来解决,但我不确定如何安全地做到这一点(也许只是删除 e.stack.toString() 的第二行??)。

更新

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

你也可以使用Object.create来代替function x(){},然后x.prototype = Error.prototype,再new x()
2021-04-24 23:03:31
而且我认为没有this必要返回
2021-04-28 23:03:31
这是(可悲的是)正确的做法。接受的答案并未将其设置为 MyError 类型,而只是创建了一个 Error 类型。所以自定义错误处理是不可能的。
2021-05-03 23:03:31
你为什么需要IntermediateInheritor
2021-05-04 23:03:31
我糊涂了。如果Error不操纵this,你为什么要做Error.apply(this, arguments)而不是Error.apply(null, arguments)
2021-05-08 23:03:31
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 } 
});
-1 表示没有正确子类化并且instanceof没有工作。请参阅下面的答案以获得更完整的解决方案
2021-04-27 23:03:31
“下面的答案”是相对的。2014 年 6 月 27 日以下是什么?:D
2021-04-29 23:03:31
感谢您的回答,以及 Object.create 继承技巧!
2021-05-01 23:03:31
这并不完全正确。不使用new关键字只会导致Error构造函数使用关键字递归调用自身new,这反过来又使堆栈跟踪在Error构造函数内部开始,而不是在您自己的代码中初始化调用的位置。因此,始终使用new关键字。
2021-05-14 23:03:31
-1 用于在没有解释的情况下宣布某事是不好的做法(据我所知,这可能是一件非常愚蠢的事情,但我不知道为什么阅读您的答案)以及推荐Object.create作为替代方案而不承认它是在 IE 8 及更低版本中不受支持。
2021-05-20 23:03:31

在 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

您可以使用 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 ...>

在 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
    }
}
这在 2012 年被问及这个问题时是不可能的。这是一个关于 ES6 的类似问题:stackoverflow.com/questions/31089801/...
2021-05-13 23:03:31