我可以在JavaScript中为用户定义的异常定义自定义类型吗?如果是,我该怎么做?


当前回答

用于ES2015类的asselin答案的替代方案

class InvalidArgumentException extends Error {
    constructor(message) {
        super();
        Error.captureStackTrace(this, this.constructor);
        this.name = "InvalidArgumentException";
        this.message = message;
    }
}

其他回答

我经常使用带有原型继承的方法。重写toString()的好处是,像Firebug这样的工具将记录实际信息,而不是将[object object]记录到控制台,以查找未捕获的异常。

使用instanceof来确定异常的类型。

main.js

// just an exemplary namespace
var ns = ns || {};

// include JavaScript of the following
// source files here (e.g. by concatenation)

var someId = 42;
throw new ns.DuplicateIdException('Another item with ID ' +
    someId + ' has been created');
// Firebug console:
// uncaught exception: [Duplicate ID] Another item with ID 42 has been created

Exception.js

ns.Exception = function() {
}

/**
 * Form a string of relevant information.
 *
 * When providing this method, tools like Firebug show the returned 
 * string instead of [object Object] for uncaught exceptions.
 *
 * @return {String} information about the exception
 */
ns.Exception.prototype.toString = function() {
    var name = this.name || 'unknown';
    var message = this.message || 'no description';
    return '[' + name + '] ' + message;
};

DuplicateIdException.js

ns.DuplicateIdException = function(message) {
    this.name = 'Duplicate ID';
    this.message = message;
};

ns.DuplicateIdException.prototype = new ns.Exception();

使用throw语句。

JavaScript并不关心异常类型(就像Java一样)。 JavaScript只是注意到,有一个异常,当你捕捉到它,你可以“看看”异常“说”什么。

如果你有不同的异常类型,你必须抛出,我建议使用变量包含异常的字符串/对象,即消息。在需要它的地方使用“throw myException”,在catch中,将捕获的异常与myException进行比较。

简而言之:

如果你使用的是不带转译器的ES6: 类CustomError扩展错误{/*…* /} 关于当前的最佳实践,请参见使用ES6语法扩展Javascript错误 如果你正在使用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) { const 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

你应该创建一个扩展Error的自定义异常:

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

注意:设置this.name是. tostring()和.stack打印InvalidArgumentException而不是Error所必需的。

对于旧的Javascript环境,你可以做原型继承:

function InvalidArgumentException(message) {
    this.message = message;
    // Use V8's native method if available, otherwise fallback
    if ("captureStackTrace" in Error)
        Error.captureStackTrace(this, InvalidArgumentException);
    else
        this.stack = (new Error()).stack;
}

InvalidArgumentException.prototype = Object.create(Error.prototype);
InvalidArgumentException.prototype.name = "InvalidArgumentException";
InvalidArgumentException.prototype.constructor = InvalidArgumentException;

这基本上是一个简化版本的disfated与增强堆栈跟踪工作在Firefox和其他浏览器。

两者都满足所有这些测试:

用法:

throw new InvalidArgumentException();
var err = new InvalidArgumentException("Not yet...");

它的行为是预期的:

err instanceof InvalidArgumentException          // -> true
err instanceof Error                             // -> true
InvalidArgumentException.prototype.isPrototypeOf(err) // -> true
Error.prototype.isPrototypeOf(err)               // -> true
err.constructor.name                             // -> InvalidArgumentException
err.name                                         // -> InvalidArgumentException
err.message                                      // -> Not yet...
err.toString()                                   // -> InvalidArgumentException: Not yet...
err.stack                                        // -> works fine!

用于ES2015类的asselin答案的替代方案

class InvalidArgumentException extends Error {
    constructor(message) {
        super();
        Error.captureStackTrace(this, this.constructor);
        this.name = "InvalidArgumentException";
        this.message = message;
    }
}