我想在我的JS代码中抛出一些东西,我想让它们成为instanceof Error,但我也想让它们成为其他东西。

在Python中,通常会子类化Exception。

在JS中应该做什么?


当前回答

我建议的解决方案是使用error的.name属性来区分错误类型,而不是instancof

这并没有完全回答问题,但我认为这是一个合理的解决方案,至少在某些情况下。

我所看到的能够拥有一个instanceof CustomError的好处是,您可以在promise catch处理程序中进行自定义处理。

例如:

class CustomError extends Error {/** ... **/}

axios
  .post(url, payload)
  .then(data => {
    if (!data.loggedIn) throw CustomError("not logged in");
    return data;
  })
  .catch(error => {
    if (error instanceof CustomError) {/** custom handling of error*//}
    throw error
  })

如果这是你想要达到的目的,.name参数也很适合你:

export const ERROR_NOT_LOGGED_IN = "ERROR_NOT_LOGGED_IN";

axios
  .post(url, payload)
  .then(data => {
    if (!data.loggedIn) throw Error("not logged in").name=ERROR_NOT_LOGGED_IN ;
    return data;
  })
  .catch(error => {
    if (error.name === ERROR_NOT_LOGGED_IN) {/** custom handling of error*//}
    throw error
  })

其他回答

重要提示:这个答案来自2016年,现在可能已经过时了。JavaScript总体上,特别是Node.js进步了很多,现在提供了更多的语法可能性来实现相同的结果。下面的内容是出于历史原因而保留的,只是为了以防有人正在处理遗留的Node.js版本。


最初的回答:

为了完整起见——因为之前的回答都没有提到这个方法——如果你正在使用Node.js,不必关心浏览器兼容性,那么使用内置的util模块继承(官方文档在这里)就很容易达到预期的效果。

例如,假设你想要创建一个自定义错误类,将错误代码作为第一个参数,错误消息作为第二个参数:

文件custom-error.js:

'use strict';

var util = require('util');

function CustomError(code, message) {
  Error.captureStackTrace(this, CustomError);
  this.name = CustomError.name;
  this.code = code;
  this.message = message;
}

util.inherits(CustomError, Error);

module.exports = CustomError;

现在你可以实例化并传递/抛出你的CustomError:

var CustomError = require('./path/to/custom-error');

// pass as the first argument to your callback
callback(new CustomError(404, 'Not found!'));

// or, if you are working with try/catch, throw it
throw new CustomError(500, 'Server Error!');

注意,对于这个代码片段,堆栈跟踪将具有正确的文件名和行,错误实例将具有正确的名称!

这是由于使用了captureStackTrace方法,该方法在目标对象上创建了一个堆栈属性(在本例中,实例化了CustomError)。有关它如何工作的更多细节,请查看这里的文档。

Crescent Fresh的答案是误导性的。尽管他的警告是无效的,但还有其他一些限制他没有提到。

首先,Crescent的“警告:”段落中的推理没有意义。这种解释意味着,与多个catch语句相比,编码“一堆if (error instanceof MyError) else…”在某种程度上是累赘或冗长的。单个catch块中的多个instanceof语句与多个catch语句一样简洁——干净简洁的代码,没有任何技巧。这是模拟Java出色的特定于可抛出子类型的错误处理的好方法。

WRT“显示子类的消息属性没有得到设置”,如果使用正确构造的Error子类,则不会出现这种情况。要创建自己的ErrorX Error子类,只需复制以"var MyError ="开头的代码块,将"MyError"改为"ErrorX"。(如果您想向子类添加自定义方法,请遵循示例文本)。

The real and significant limitation of JavaScript error subclassing is that for JavaScript implementations or debuggers that track and report on stack trace and location-of-instantiation, like FireFox, a location in your own Error subclass implementation will be recorded as the instantiation point of the class, whereas if you used a direct Error, it would be the location where you ran "new Error(...)"). IE users would probably never notice, but users of Fire Bug on FF will see useless file name and line number values reported alongside these Errors, and will have to drill down on the stack trace to element #1 to find the real instantiation location.

简而言之:

如果你使用的是不带转译器的ES6: 类CustomError扩展错误{/*…* /} 如果你正在使用Babel转译器:

选项1:使用babel-plugin-transform-builtin-extend

选项2:自己动手(灵感来自同一个库)

    function CustomError(...args) {
      const instance = Reflect.construct(Error, args);
      Reflect.setPrototypeOf(instance, Reflect.getPrototypeOf(this));
      return instance;
    }
    CustomError.prototype = Object.create(Error.prototype, {
      constructor: {
        value: Error,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
    Reflect.setPrototypeOf(CustomError, Error);

If you are using pure ES5: function CustomError(message, fileName, lineNumber) { var instance = new Error(message, fileName, lineNumber); Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); return instance; } CustomError.prototype = Object.create(Error.prototype, { constructor: { value: Error, enumerable: false, writable: true, configurable: true } }); if (Object.setPrototypeOf){ Object.setPrototypeOf(CustomError, Error); } else { CustomError.__proto__ = Error; } Alternative: use Classtrophobic framework

解释:

为什么使用ES6和Babel扩展Error类是一个问题?

因为CustomError的实例不再被这样识别。

class CustomError extends Error {}
console.log(new CustomError('test') instanceof Error);// true
console.log(new CustomError('test') instanceof CustomError);// false

事实上,在Babel的官方文档中,你不能扩展任何内置的JavaScript类,如Date、Array、DOM或Error。

问题描述如下:

本机扩展破坏HTMLELement, Array和其他 类的一个对象,它扩展了基类型如数组,数字,对象,字符串或错误不是这个类的实例

那么其他的SO答案呢?

所有给出的答案都修复了instanceof问题,但你会失去常规的错误console.log:

console.log(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵    at CustomError (<anonymous>:4:19)↵    at <anonymous>:1:5"}

而使用上面提到的方法,不仅可以修复instanceof问题,还可以保留常规的error console.log:

console.log(new CustomError('test'));
// output:
// Error: test
//     at CustomError (<anonymous>:2:32)
//     at <anonymous>:1:5

编辑:请阅读评论。我的目的是提供一个跨浏览器的解决方案,它可以在所有浏览器中工作,并在支持的地方提供堆栈跟踪。

编辑:我创建这个社区维基是为了允许更多的编辑。

V8 (Chrome / Node.JS)的解决方案,在Firefox中工作,并可以修改为在IE中正常工作。(见文末)

function UserError(message) {
  this.constructor.prototype.__proto__ = Error.prototype // Make this an instanceof Error.
  Error.call(this) // Does not seem necessary. Perhaps remove this line?
  Error.captureStackTrace(this, this.constructor) // Creates the this.stack getter
  this.name = this.constructor.name; // Used to cause messages like "UserError: message" instead of the default "Error: message"
  this.message = message; // Used to set the message
}

“让我看看代码!”

短版:

function UserError(message) {
  this.constructor.prototype.__proto__ = Error.prototype
  Error.captureStackTrace(this, this.constructor)
  this.name = this.constructor.name
  this.message = message
}

我保留这个。构造器。原型。__proto__ =错误。函数中的原型将所有代码保存在一起。但是你也可以替换这个。使用UserError构造函数,这允许你将代码移到函数之外,所以它只被调用一次。

如果您走那条路,请确保在第一次抛出UserError之前调用该行。

这个警告并不适用于函数,因为函数是先创建的,不管顺序如何。因此,您可以将函数移动到文件的末尾,没有任何问题。

浏览器兼容性

工作在Firefox和Chrome(和Node.JS),并满足所有承诺。

ie浏览器出现如下问题

Errors do not have err.stack to begin with, so "it's not my fault". Error.captureStackTrace(this, this.constructor) does not exist so you need to do something else like if(Error.captureStackTrace) // AKA if not IE Error.captureStackTrace(this, this.constructor) toString ceases to exist when you subclass Error. So you also need to add. else this.toString = function () { return this.name + ': ' + this.message } IE will not consider UserError to be an instanceof Error unless you run the following some time before you throw UserError UserError.prototype = Error.prototype

因为JavaScript异常很难子类化,所以我不子类化。我只是创建了一个新的Exception类,并在其中使用一个Error。我修改了Error.name属性,使它看起来像我在控制台上的自定义异常:

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

上面的new异常可以像普通的Error一样被抛出,它会像预期的那样工作,例如:

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

注意:堆栈跟踪并不是完美的,因为它只会把你带到创建新错误的地方,而不是你抛出错误的地方。这在Chrome上不是什么大问题,因为它直接在控制台中为您提供了完整的堆栈跟踪。但在Firefox上问题就更多了。