什么's一个很好的方法来扩展错误在JavaScript?

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

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

在JS中应该做什么?

193709 次浏览

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

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

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

JavaScript错误子类化的真正而重要的限制是,对于跟踪和报告堆栈跟踪和实例化位置的JavaScript实现或调试器,如FireFox,你自己的错误子类实现中的位置将被记录为类的实例化点,而如果你使用直接错误,它将是你运行“new error(…)”的位置。IE用户可能永远不会注意到,但是FF上的Fire Bug用户将看到无用的文件名和行号值与这些错误一起报告,并且必须在堆栈跟踪中向下钻取到元素#1以找到真正的实例化位置。

在上面的例子中Error.apply(也是Error.call)对我没有任何作用(Firefox 3.6/Chrome 5)。我使用的一个变通方法是:

function MyError(message, fileName, lineNumber) {
var err = new Error();


if (err.stack) {
// remove one stack level:
if (typeof(Components) != 'undefined') {
// Mozilla:
this.stack = err.stack.substring(err.stack.indexOf('\n')+1);
}
else if (typeof(chrome) != 'undefined' || typeof(process) != 'undefined') {
// Google Chrome/Node.js:
this.stack = err.stack.replace(/\n[^\n]*/,'');
}
else {
this.stack = err.stack;
}
}
this.message    = message    === undefined ? err.message    : message;
this.fileName   = fileName   === undefined ? err.fileName   : fileName;
this.lineNumber = lineNumber === undefined ? err.lineNumber : lineNumber;
}


MyError.prototype = new Error();
MyError.prototype.constructor = MyError;
MyError.prototype.name = 'MyError';

Error对象的唯一标准字段是message属性。(参见中数,或EcmaScript语言规范,章节15.11)

大多数环境设置stack属性,但是fileNamelineNumber在继承中实际上是无用的。

所以,极简主义的方法是:

function MyError(message) {
this.name = 'MyError';
this.message = message;
this.stack = (new Error()).stack;
}
MyError.prototype = new Error;  // <-- remove this if you do not
//     want MyError to be instanceof Error

您可以嗅探堆栈,从堆栈中移除不需要的元素,并提取文件名和lineNumber等信息,但这样做需要有关JavaScript当前运行的平台的信息。大多数情况下,这是不必要的——如果你真的想,你可以在事后进行分析。

Safari是一个明显的例外。没有stack属性,但是throw关键字设置了被抛出对象的sourceURLline属性。这些东西肯定是正确的。

我使用的测试用例可以在这里找到:JavaScript自制错误对象比较

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

编辑:我让这个社区维基允许更多的编辑。

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
}

我在函数中保留this.constructor.prototype.__proto__ = Error.prototype,以保持所有代码在一起。但是你也可以用UserError替换this.constructor,这样你就可以把代码移到函数之外,所以它只被调用一次。

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

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

浏览器兼容性

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

ie浏览器出现如下问题

  • 错误一开始就没有err.stack,所以“这不是我的错”。

  • Error.captureStackTrace(this, this.constructor)不存在,所以你需要做一些其他的事情,比如

    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
    

正确的方法是从构造函数返回apply的结果,以及以通常复杂的javascript方式设置原型:

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

在这一点上,这种方法唯一的问题(我已经重复了一点)是

  • stackmessage以外的属性不包含在MyError
  • 堆栈跟踪有一个额外的行,这并不是真正必要的。

第一个问题可以通过使用以下回答中的技巧迭代所有不可枚举的error属性来解决:是否有可能获得一个对象的不可枚举的继承属性名?,但ie<9不支持这一点。第二个问题可以通过在堆栈跟踪中删除这一行来解决,但我不确定如何安全地做到这一点(可能只是删除e.stack.toString()的第二行??)

我想再补充一下大家已经说过的话:

为了确保自定义错误类在堆栈跟踪中正确显示,您需要将自定义错误类的原型的name属性设置为自定义错误类的name属性。 这就是我的意思:

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

完整的例子是:

    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';

当所有的都说了,做了,你抛出你的新异常,它看起来像这样(我懒惰地尝试在chrome开发工具):

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)

因为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上问题就更多了。

我的解决方案比其他提供的答案更简单,也没有缺点。

它保留了Error原型链和Error上的所有属性,而不需要对它们有具体的了解。它已经在Chrome, Firefox, Node和IE11中进行了测试。

唯一的限制是在调用堆栈的顶部有一个额外的条目。但这很容易被忽视。

下面是一个带有两个自定义参数的例子:

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

使用示例:

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
}

对于需要setPrototypeOf的polyfil的环境:

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

我的观点是:

为什么又是另一个答案?

a)因为访问Error.stack属性(在一些答案中)有很大的性能损失。

b)因为只有一行。

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

使用的例子

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

它能做什么?

this.__proto__.__proto__MyError.prototype.__proto__,所以它为所有实例设置__proto__ MyError的一个特定的新创建的错误。它保留MyError类的属性和方法,并将新的Error属性(包括.stack)放在__proto__链中

明显的问题:

你不能有一个以上的MyError实例和有用的堆栈信息。

如果你不完全理解this.__proto__.__proto__=的作用,不要使用这个解决方案。

这个解决方案怎么样?

而不是抛出你的自定义错误使用:

throw new MyError("Oops!");

你可以包装Error对象(有点像Decorator):

throw new MyError(Error("Oops!"));

这将确保所有属性都是正确的,例如堆栈、fileName lineNumber等等。

你所要做的就是复制属性,或者为它们定义getter。 下面是一个使用getter (IE9)的例子:

function MyError(wrapped)
{
this.wrapped = wrapped;
this.wrapped.name = 'MyError';
}


function wrap(attr)
{
Object.defineProperty(MyError.prototype, attr, {
get: function()
{
return this.wrapped[attr];
}
});
}


MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;


wrap('name');
wrap('message');
wrap('stack');
wrap('fileName');
wrap('lineNumber');
wrap('columnNumber');


MyError.prototype.toString = function()
{
return this.wrapped.toString();
};

我会退一步考虑你为什么要这么做?我认为关键是要以不同的方式处理不同的错误。

例如,在Python中,你可以限制catch语句只捕获MyValidationError,也许你想在javascript中能够做类似的事情。

catch (MyValidationError e) {
....
}

你不能在javascript中这样做。只有一个捕捉块。你应该对错误使用if语句来确定它的类型。

< p > <代码> 抓住(e) { 如果(isMyValidationError (e)) { ... }其他{ //可能重新抛出? 把e; } } < /代码> < / p >

我想我会抛出一个原始对象,其中包含类型、消息和您认为合适的任何其他属性。

throw { type: "validation", message: "Invalid timestamp" }

当你捕捉到错误时:

catch(e) {
if(e.type === "validation") {
// handle error
}
// re-throw, or whatever else
}

为了针对每种不同类型的错误避免使用样板文件,我将一些解决方案的智慧结合到createErrorType函数中:

function createErrorType(name, init) {
function E(message) {
if (!Error.captureStackTrace)
this.stack = (new Error()).stack;
else
Error.captureStackTrace(this, this.constructor);
this.message = message;
init && init.apply(this, arguments);
}
E.prototype = new Error();
E.prototype.name = name;
E.prototype.constructor = E;
return E;
}

然后你可以像下面这样轻松定义新的错误类型:

var NameError = createErrorType('NameError', function (name, invalidChar) {
this.message = 'The name ' + name + ' may not contain ' + invalidChar;
});


var UnboundError = createErrorType('UnboundError', function (variableName) {
this.message = 'Variable ' + variableName + ' is not bound';
});

自定义错误装饰器

这是基于乔治·贝利的回答,但扩展和简化了原来的想法。它是用CoffeeScript编写的,但是很容易转换为JavaScript。其思想是用包装Bailey的自定义错误的装饰器来扩展它,允许您轻松地创建新的自定义错误。

注意:这将只在V8中工作。在其他环境中不支持Error.captureStackTrace

定义

装饰器接受错误类型的名称,并返回一个接受错误消息并包含错误名称的函数。

CoreError = (@message) ->


@constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace @, @constructor
@name = @constructor.name


BaseError = (type) ->


(message) -> new CoreError "#{ type }Error: #{ message }"

使用

现在创建新的错误类型就很简单了。

StorageError   = BaseError "Storage"
SignatureError = BaseError "Signature"

为了好玩,你现在可以定义一个函数,如果调用了太多参数,就会抛出SignatureError

f = -> throw SignatureError "too many args" if arguments.length

这已经被测试得很好,似乎在V8上工作得很好,保持回溯,位置等。

注意:在构造自定义错误时,使用new是可选的。

在ES6:

class MyError extends Error {
constructor(message) {
super(message);
this.name = 'MyError';
}
}

< a href = " http://azimi.me/2015/09/23/high-custom-error-class-es6.html " > < / >来源

2018中,我认为这是最好的方法;支持IE9+和现代浏览器。

更新:不同实现的比较请参见这个测试回购

function CustomError(message) {
Object.defineProperty(this, 'name', {
enumerable: false,
writable: false,
value: 'CustomError'
});


Object.defineProperty(this, 'message', {
enumerable: false,
writable: true,
value: message
});


if (Error.hasOwnProperty('captureStackTrace')) { // V8
Error.captureStackTrace(this, CustomError);
} else {
Object.defineProperty(this, 'stack', {
enumerable: false,
writable: false,
value: (new Error(message)).stack
});
}
}


if (typeof Object.setPrototypeOf === 'function') {
Object.setPrototypeOf(CustomError.prototype, Error.prototype);
} else {
CustomError.prototype = Object.create(Error.prototype, {
constructor: { value: CustomError }
});
}

还要注意__proto__属性是deprecated,这在其他答案中广泛使用。

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


最初的回答:

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

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

文件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方法,该方法在目标对象上创建了stack属性(在这种情况下,CustomError被实例化)。有关它如何工作的更多细节,请查看文档在这里

在Mohsen的回答中指出,在ES6中可以使用类来扩展错误。这要容易得多,而且它们的行为与本机错误更一致……但不幸的是,如果你需要支持前es6版本的浏览器,在浏览器中使用它并不是一件简单的事情。下面是关于如何实施的一些注意事项,但与此同时,我建议一个相对简单的方法,它包含了其他答案中的一些最好的建议:

function CustomError(message) {
//This is for future compatibility with the ES6 version, which
//would display a similar message if invoked without the
//`new` operator.
if (!(this instanceof CustomError)) {
throw new TypeError("Constructor 'CustomError' cannot be invoked without 'new'");
}
this.message = message;


//Stack trace in V8
if (Error.captureStackTrace) {
Error.captureStackTrace(this, CustomError);
}
else this.stack = (new Error).stack;
}
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.name = 'CustomError';

在ES6中,这很简单:

class CustomError extends Error {}

...并且你可以通过try {eval('class X{}')检测对ES6类的支持,但是如果你试图在旧浏览器加载的脚本中包含ES6版本,你会得到一个语法错误。因此,支持所有浏览器的唯一方法是为支持ES6的浏览器动态加载单独的脚本(例如通过AJAX或eval())。更复杂的是,eval()在所有环境中都不支持(由于内容安全策略),这可能是也可能不是你的项目的考虑因素。

因此,目前为止,对于需要支持非es6浏览器的代码来说,无论是上述第一种方法,还是直接使用Error而不尝试扩展它,似乎都是最好的方法。

有些人可能会考虑另一种方法,即在可用的地方使用Object.setPrototypeOf()来创建一个错误对象,该错误对象是自定义错误类型的实例,但其外观和行为更像控制台上的本机错误(感谢本的回答的推荐)。下面是我对这种方法的看法:https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8。但是考虑到有一天我们可以只使用ES6,我个人不确定这种方法的复杂性是否值得。

简而言之:

  • 如果你正在使用ES6 没有transpilers:

    class CustomError extends Error { /* ... */}
    
  • If you are using Babel transpiler:

Option 1: use babel-plugin-transform-builtin-extend

Option 2: do it yourself (inspired from that same library)

    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);
  • 如果你正在使用纯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

Explanation:

Why extending the Error class using ES6 and Babel is a problem?

Because an instance of CustomError is not anymore recognized as such.

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

事实上,从Babel的官方文档中,你不能扩展任何内置JavaScript类,如DateArrayDOMError

问题描述如下:

那么其他的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问题,还可以保留常规错误console.log:

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

就像有些人说的,ES6很简单:

class CustomError extends Error { }

所以我在我的应用程序(Angular, Typescript)中尝试了这个,但它不起作用。经过一段时间后,我发现问题来自Typescript:O

看到https://github.com/Microsoft/TypeScript/issues/13965

这很令人不安,因为如果你这样做:

class CustomError extends Error {}
​


try {
throw new CustomError()
} catch(e) {
if (e instanceof CustomError) {
console.log('Custom error');
} else {
console.log('Basic error');
}
}

在节点中或直接在浏览器中显示:Custom error

尝试在你的项目中使用Typescript在Typescript游乐场上运行它,它将显示Basic error

解决方法如下:

class CustomError extends Error {
// we have to do the following because of: https://github.com/Microsoft/TypeScript/issues/13965
// otherwise we cannot use instanceof later to catch a given type
public __proto__: Error;


constructor(message?: string) {
const trueProto = new.target.prototype;
super(message);


this.__proto__ = trueProto;
}
}

这段代码显示了这一切。

function add(x, y) {
if (x && y) {
return x + y;
} else {
/**
*
* the error thrown will be instanceof Error class and InvalidArgsError also
*/
throw new InvalidArgsError();
// throw new Invalid_Args_Error();
}
}


// Declare custom error using using Class
class Invalid_Args_Error extends Error {
constructor() {
super("Invalid arguments");
Error.captureStackTrace(this);
}
}


// Declare custom error using Function
function InvalidArgsError(message) {
this.message = `Invalid arguments`;
Error.captureStackTrace(this);
}
// does the same magic as extends keyword
Object.setPrototypeOf(InvalidArgsError.prototype, Error.prototype);


try{
add(2)
}catch(e){
// true
if(e instanceof Error){
console.log(e)
}
// true
if(e instanceof InvalidArgsError){
console.log(e)
}
}

正如其他人所说,在Node中,这很简单:

class DumbError extends Error {
constructor(foo = 'bar', ...params) {
super(...params);


if (Error.captureStackTrace) {
Error.captureStackTrace(this, DumbError);
}


this.name = 'DumbError';


this.foo = foo;
this.date = new Date();
}
}


try {
let x = 3;
if (x < 10) {
throw new DumbError();
}
} catch (error) {
console.log(error);
}

如果你不关心错误的性能,这是你能做的最小的

Object.setPrototypeOf(MyError.prototype, Error.prototype)
function MyError(message) {
const error = new Error(message)
Object.setPrototypeOf(error, MyError.prototype);
return error
}

你可以不使用new只使用MyError(message)

通过在构造函数Error被调用后更改原型,我们不必设置调用堆栈和消息

Mohsen在ES6中有一个很好的答案,它设置了名称,但如果你正在使用TypeScript,或者如果你生活在未来,希望这个公共类和私有类字段的建议已经作为提案通过了阶段3,并作为ECMAScript/JavaScript的一部分进入了阶段4,那么你可能想知道这只是稍微短一点。阶段3是浏览器开始实现特性的地方,所以如果你的浏览器支持它,下面的代码就可以工作了。(在新的Edge浏览器v81中进行了测试,似乎可以正常工作)。不过要注意的是,目前这是一个不稳定的特性,应该谨慎使用,你应该总是检查浏览器对不稳定特性的支持。这篇文章主要是为那些未来的居民,当浏览器可能支持这个。要检查支持,检查中数我可以用。它目前在整个浏览器市场上得到了66%的支持,正在接近这个水平,但还不是很好,所以如果你真的想现在就使用它,不想等待,可以使用巴别塔打印稿这样的编译器。

class EOFError extends Error {
name="EOFError"
}
throw new EOFError("Oops errored");

将此与一个无名错误进行比较,该错误在抛出时不会记录其名称。

class NamelessEOFError extends Error {}
throw new NamelessEOFError("Oops errored");

我不喜欢所有其他的答案,太长,太复杂,或者没有正确地跟踪堆栈。这里是我的方法,如果你需要更多的自定义道具,将它们传递给构造函数,并像name一样设置它们。

class CustomError extends Error {
constructor (message) {
super(message)


// needed for CustomError instanceof Error => true
Object.setPrototypeOf(this, new.target.prototype);


// Set the name
this.name = this.constructor.name


// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor)
}
}
}


// create own CustomError sub classes
class SubCustomError extends CustomError{}


// Tests
console.log(new SubCustomError instanceof CustomError) // true
console.log(new SubCustomError instanceof CustomError) // true
console.log(new CustomError instanceof Error) // true
console.log(new SubCustomError instanceof Error) // true


throw new SubCustomError ('test 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
})

这并不复杂,但我个人认为这是扩展错误的最简单方法。

export default class ExtendableError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}

创建一个类似ExtendableError的实用程序类。这个实用程序类的目的是像普通的Error类一样,但默认情况下将name属性更改为类名,因此很容易扩展错误。

现在,如果您想扩展一个错误,它只需要一行。

class MyError extends ExtendableError {}

除了标准的message属性外,JavaScript现在支持将特定的cause错误作为可选参数添加到Error构造函数中:

const error1 = new Error('Error one');
const error2 = new Error('Error two', { cause: error1 });
// error2.cause === error1