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

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

在JS中应该做什么?


当前回答

我的观点是:

为什么又是另一个答案?

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__ =。

其他回答

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

正确的方法是从构造函数返回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 ...>

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

除了stack和message之外的属性不包含在MyError和 堆栈跟踪有一个额外的行,这并不是真正必要的。

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

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

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

我的观点是:

为什么又是另一个答案?

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__ =。

为了避免针对每种不同类型的错误的样板文件,我将一些解决方案的智慧结合到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';
});