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

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

在JS中应该做什么?


当前回答

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

例如,在Python中,您可以限制catch语句仅捕获MyValidationError,也许您希望能够在javascript中执行类似的操作。

catch (MyValidationError e) {
    ....
}

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

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

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

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

当你捕捉到错误时:

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

其他回答

我的观点是:

为什么又是另一个答案?

a)因为访问错误。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__ =。

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

它保留了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;
};

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

自定义错误装饰器

这是基于George Bailey的回答,但扩展并简化了最初的想法。它是用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 }"

Use

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

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