什么是在JavaScript中扩展错误的好方法?

本文翻译自:What's a good way to extend Error in JavaScript?

I want to throw some things in my JS code and I want them to be instanceof Error, but I also want to have them be something else. 我想在我的JS代码中抛出一些东西,我希望它们是instanceof Error,但我也想让它们成为别的东西。

In Python, typically, one would subclass Exception. 在Python中,通常会有一个子类Exception。

What's the appropriate thing to do in JS? 在JS中做什么是合适的?


#1楼

参考:https://stackoom.com/question/5nY3/什么是在JavaScript中扩展错误的好方法


#2楼

The way to do this right is to return the result of the apply from the constructor, as well as setting the prototype in the usual complicated javascripty way: 这样做的方法是从构造函数返回apply的结果,以及以通常复杂的javascripty方式设置原型:

function MyError() {
    var tmp = Error.apply(this, arguments);
    tmp.name = this.name = 'MyError'

    this.stack = tmp.stack
    this.message = tmp.message

    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 iterated it a bit) are that 这种方式在这一点上做的唯一问题(我已经迭代了一点)就是这样

  • properties other than stack and message aren't included in MyError and 除了stackmessage之外的属性不包含在MyError
  • the stacktrace has an additional line that isn't really necessary. stacktrace有一个额外的行,这不是必需的。

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. ,但<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() ??). 第二个问题可以通过撕掉堆栈跟踪中的那条线来解决,但我不确定如何安全地执行此操作(可能只是删除了第二行的e.stack.toString()??)。


#3楼

I just want to add to what others have already stated: 我只想补充其他人已经说过的内容:

To make sure that the custom error class shows up properly in the stack trace, you need to set the custom error class's prototype's name property to the custom error class's name property. 要确保自定义错误类在堆栈跟踪中正确显示,您需要将自定义错误类的prototype的name属性设置为自定义错误类的name属性。 This is what I mean: 这就是我的意思:

CustomError.prototype = Error.prototype;
CustomError.prototype.name = 'CustomError';

So the full example would be: 所以完整的例子是:

    var CustomError = function(message) {
        var err = new Error(message);
        err.name = 'CustomError';
        this.name = err.name;
        this.message = err.message;
        //check if there is a stack property supported in browser
        if (err.stack) {
            this.stack = err.stack;
        }
        //we should define how our toString function works as this will be used internally
        //by the browser's stack trace generation function
        this.toString = function() {
           return this.name + ': ' + this.message;
        };
    };
    CustomError.prototype = new Error();
    CustomError.prototype.name = 'CustomError';

When all is said and done, you throw your new exception and it looks like this (I lazily tried this in the chrome dev tools): 完成所有操作后,抛出新的异常,看起来就像这样(我在chrome dev工具中懒得尝试过这个):

CustomError: Stuff Happened. GASP!
    at Error.CustomError (<anonymous>:3:19)
    at <anonymous>:2:7
    at Object.InjectedScript._evaluateOn (<anonymous>:603:39)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:562:52)
    at Object.InjectedScript.evaluate (<anonymous>:481:21)

#4楼

Since JavaScript Exceptions are difficult to sub-class, I don't sub-class. 由于JavaScript Exceptions很难分类,所以我不用子类。 I just create a new Exception class and use an Error inside of it. 我只是创建一个新的Exception类并在其中使用Error。 I change the Error.name property so that it looks like my custom exception on the console: 我更改了Error.name属性,使其在控制台上看起来像我的自定义异常:

var InvalidInputError = function(message) {
    var error = new Error(message);
    error.name = 'InvalidInputError';
    return error;
};

The above new exception can be thrown just like a regular Error and it will work as expected, for example: 上面的新异常可以像常规错误一样抛出,它会按预期工作,例如:

throw new InvalidInputError("Input must be a string");
// Output: Uncaught InvalidInputError: Input must be a string 

Caveat: the stack trace is not perfect, as it will bring you to where the new Error is created and not where you throw. 警告:堆栈跟踪并不完美,因为它会将您带到创建新错误的位置而不是您抛出的位置。 This is not a big deal on Chrome because it provides you with a full stack trace directly in the console. 这对Chrome来说不是什么大问题,因为它直接在控制台中为您提供完整的堆栈跟踪。 But it's more problematic on Firefox, for example. 但是,例如,Firefox上的问题更多。


#5楼

My solution is more simple than the other answers provided and doesn't have the downsides. 我的解决方案比提供的其他答案更简单,并没有缺点。

It preserves the Error prototype chain and all properties on Error without needing specific knowledge of them. 它保留Error原型链和Error上的所有属性,而无需具体了解它们。 It's been tested in Chrome, Firefox, Node, and IE11. 它已经过Chrome,Firefox,Node和IE11的测试。

The only limitation is an extra entry at the top of the call stack. 唯一的限制是调用堆栈顶部的额外条目。 But that is easily ignored. 但这很容易被忽视。

Here's an example with two custom parameters: 这是一个包含两个自定义参数的示例:

function CustomError(message, param1, param2) {
    var err = new Error(message);
    Object.setPrototypeOf(err, CustomError.prototype);

    err.param1 = param1;
    err.param2 = param2;

    return err;
}

CustomError.prototype = Object.create(
    Error.prototype,
    {name: {value: 'CustomError', enumerable: false}}
);

Example Usage: 用法示例:

try {
    throw new CustomError('Something Unexpected Happened!', 1234, 'neat');
} catch (ex) {
    console.log(ex.name); //CustomError
    console.log(ex.message); //Something Unexpected Happened!
    console.log(ex.param1); //1234
    console.log(ex.param2); //neat
    console.log(ex.stack); //stacktrace
    console.log(ex instanceof Error); //true
    console.log(ex instanceof CustomError); //true
}

For environments that require a polyfil of setPrototypeOf: 对于需要setPrototypeOf的polyfil的环境:

Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
    obj.__proto__ = proto;
    return obj;
};

#6楼

My 2 cents: 我的2美分:

Why another answer? 为什么另一个答案

a) Because accessing the Error.stack property (as in some answers) have a large performance penalty. a)因为访问Error.stack属性(如在某些答案中)具有很大的性能损失。

b) Because it is only one line. b)因为它只有一条线。

c) Because the solution at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error does not seems to preserve stack info. c)因为https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error上的解决方案似乎不保留堆栈信息。

//MyError class constructor
function MyError(msg){
    this.__proto__.__proto__ = Error.apply(null, arguments);
};

usage example 用法示例

http://jsfiddle.net/luciotato/xXyeB/ http://jsfiddle.net/luciotato/xXyeB/

What does it do? 它有什么作用?

this.__proto__.__proto__ is MyError.prototype.__proto__ , so it is setting the __proto__ FOR ALL INSTANCES of MyError to a specific newly created Error. this.__proto__.__proto__MyError.prototype.__proto__ ,因此它将__proto__ FOR MyError的所有实例设置为特定的新创建的错误。 It keeps MyError class properties and methods and also puts the new Error properties (including .stack) in the __proto__ chain. 它保留MyError类属性和方法,并将新的Error属性(包括.stack)放在__proto__链中。

Obvious problem: 明显的问题:

You can not have more than one instance of MyError with useful stack info. 您不能拥有多个MyError实例和有用的堆栈信息。

Do not use this solution if you do not fully understand what this.__proto__.__proto__= does. 如果您不完全理解this.__proto__.__proto__=作用,请不要使用此解决方案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值