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

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

在JS中应该做什么?


当前回答

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

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: 类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

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

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

在节点v16.9.0中提供。 适用于Chrome, Firefox和Safari(见浏览器兼容性版本)

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

在上面的例子中。apply (also 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';

在ES6:

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