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

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

在JS中应该做什么?


当前回答

这段代码显示了这一切。

function add(x, y) {
      if (x && y) {
        return x + y;
      } else {
        /**
         * 
         * the error thrown will be instanceof Error class and InvalidArgsError also
         */
        throw new InvalidArgsError();
        // throw new Invalid_Args_Error(); 
      }
    }

    // Declare custom error using using Class
    class Invalid_Args_Error extends Error {
      constructor() {
        super("Invalid arguments");
        Error.captureStackTrace(this);
      }
    }

    // Declare custom error using Function
    function InvalidArgsError(message) {
      this.message = `Invalid arguments`;
      Error.captureStackTrace(this);
    }
    // does the same magic as extends keyword
    Object.setPrototypeOf(InvalidArgsError.prototype, Error.prototype);

    try{
      add(2)
    }catch(e){
      // true
      if(e instanceof Error){
        console.log(e)
      }
      // true
      if(e instanceof InvalidArgsError){
        console.log(e)
      }
    }

其他回答

这段代码显示了这一切。

function add(x, y) {
      if (x && y) {
        return x + y;
      } else {
        /**
         * 
         * the error thrown will be instanceof Error class and InvalidArgsError also
         */
        throw new InvalidArgsError();
        // throw new Invalid_Args_Error(); 
      }
    }

    // Declare custom error using using Class
    class Invalid_Args_Error extends Error {
      constructor() {
        super("Invalid arguments");
        Error.captureStackTrace(this);
      }
    }

    // Declare custom error using Function
    function InvalidArgsError(message) {
      this.message = `Invalid arguments`;
      Error.captureStackTrace(this);
    }
    // does the same magic as extends keyword
    Object.setPrototypeOf(InvalidArgsError.prototype, Error.prototype);

    try{
      add(2)
    }catch(e){
      // true
      if(e instanceof Error){
        console.log(e)
      }
      // true
      if(e instanceof InvalidArgsError){
        console.log(e)
      }
    }

Crescent Fresh的答案是误导性的。尽管他的警告是无效的,但还有其他一些限制他没有提到。

首先,Crescent的“警告:”段落中的推理没有意义。这种解释意味着,与多个catch语句相比,编码“一堆if (error instanceof MyError) else…”在某种程度上是累赘或冗长的。单个catch块中的多个instanceof语句与多个catch语句一样简洁——干净简洁的代码,没有任何技巧。这是模拟Java出色的特定于可抛出子类型的错误处理的好方法。

WRT“显示子类的消息属性没有得到设置”,如果使用正确构造的Error子类,则不会出现这种情况。要创建自己的ErrorX Error子类,只需复制以"var MyError ="开头的代码块,将"MyError"改为"ErrorX"。(如果您想向子类添加自定义方法,请遵循示例文本)。

The real and significant limitation of JavaScript error subclassing is that for JavaScript implementations or debuggers that track and report on stack trace and location-of-instantiation, like FireFox, a location in your own Error subclass implementation will be recorded as the instantiation point of the class, whereas if you used a direct Error, it would be the location where you ran "new Error(...)"). IE users would probably never notice, but users of Fire Bug on FF will see useless file name and line number values reported alongside these Errors, and will have to drill down on the stack trace to element #1 to find the real instantiation location.

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

这个解决方案怎么样?

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

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

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