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

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

在JS中应该做什么?


当前回答

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

更新:请参阅此测试和repo以了解不同实现的比较。

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__属性已弃用,该属性在其他答案中广泛使用。

其他回答

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

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被调用后更改原型,我们不必设置调用堆栈和消息

在ES6:

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

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

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

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

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

class MyError extends ExtendableError {}

重要提示:这个答案来自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)。有关它如何工作的更多细节,请查看这里的文档。

这个解决方案怎么样?

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

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