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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8802845/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 04:37:22  来源:igfitidea点击:

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

javascript

提问by Philippe Plantier

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

在 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 messageproperty, while Error.call(this, msg);does not? Sure, I can just define this.message = msgin the MyErrorconstructor, but I don't quite understand why it is not already set in the first place.

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

回答by B T

A. Like, Raynos said, The reason messageisn't being set is that Erroris a function that returns a new Error object and does notmanipulate thisin any way.

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

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:

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

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

在这一点上这样做的唯一问题(我已经迭代了一点)是

  • properties other than stackand messagearen't included in MyError, and
  • the stacktrace has an additional line that isn't really necessary.
  • 除了stackmessage不包括在MyError, 和
  • 堆栈跟踪有一个额外的行,这不是真正必要的。

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() ??).

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

Update

更新

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

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

回答by Raynos

function MyError(msg) {
    var err = Error.call(this, msg);
    err.name = "MyError";
    return err;
}

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

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

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

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

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

也是MyError.prototype.__proto__ = Error.prototype;不好的做法。利用

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

回答by Peter Dotchev

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

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

var util = require('util');

function MyError(message) {
  this.message = message;
  Error.captureStackTrace(this, MyError);
}

util.inherits(MyError, Error);

MyError.prototype.name = 'MyError';

See captureStackTracein node docs

请参阅节点文档中的captureStackTrace

回答by Fay

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

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

回答by Karen Grigoryan

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

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

回答by daphtdazz

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.

另一种方法是使新的错误实例成为 的原型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

And for my money it's a bit neater. Butnote 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.

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

回答by Jo?o Miguel

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.

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

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

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

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.

在我的项目的其他文件中,我必须在文件顶部有这个 require 行。

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