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

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

在JS中应该做什么?


当前回答

就像有些人说的,ES6很简单:

class CustomError extends Error { }

所以我在我的应用程序(Angular, Typescript)中尝试了这个,但它不起作用。经过一段时间后,我发现问题来自Typescript:O

参见https://github.com/Microsoft/TypeScript/issues/13965

这很令人不安,因为如果你这样做:

class CustomError extends Error {}
​

try {
  throw new CustomError()
} catch(e) {
  if (e instanceof CustomError) {
    console.log('Custom error');
  } else {
    console.log('Basic error');
  }
}

在节点中或直接在浏览器中显示:自定义错误

尝试在你的项目中使用Typescript在Typescript游乐场上运行,它会显示基本错误…

解决方法如下:

class CustomError extends Error {
  // we have to do the following because of: https://github.com/Microsoft/TypeScript/issues/13965
  // otherwise we cannot use instanceof later to catch a given type
  public __proto__: Error;

  constructor(message?: string) {
    const trueProto = new.target.prototype;
    super(message);

    this.__proto__ = trueProto;
  }
}

其他回答

我想再补充一下大家已经说过的话:

为了确保自定义错误类在堆栈跟踪中正确显示,您需要将自定义错误类的原型的name属性设置为自定义错误类的name属性。 我的意思是:

CustomError.prototype = Error.prototype;
CustomError.prototype.name = 'CustomError';

完整的例子是:

    var CustomError = function(message) {
        var err = new Error(message);
        err.name = 'CustomError';
        this.name = err.name;
        this.message = err.message;
        //check if there is a stack property supported in browser
        if (err.stack) {
            this.stack = err.stack;
        }
        //we should define how our toString function works as this will be used internally
        //by the browser's stack trace generation function
        this.toString = function() {
           return this.name + ': ' + this.message;
        };
    };
    CustomError.prototype = new Error();
    CustomError.prototype.name = 'CustomError';

当所有的都说了,做了,你抛出你的新异常,它看起来像这样(我懒惰地尝试在chrome开发工具):

CustomError: Stuff Happened. GASP!
    at Error.CustomError (<anonymous>:3:19)
    at <anonymous>:2:7
    at Object.InjectedScript._evaluateOn (<anonymous>:603:39)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:562:52)
    at Object.InjectedScript.evaluate (<anonymous>:481:21)

我建议的解决方案是使用error的.name属性来区分错误类型,而不是instancof

这并没有完全回答问题,但我认为这是一个合理的解决方案,至少在某些情况下。

我所看到的能够拥有一个instanceof CustomError的好处是,您可以在promise catch处理程序中进行自定义处理。

例如:

class CustomError extends Error {/** ... **/}

axios
  .post(url, payload)
  .then(data => {
    if (!data.loggedIn) throw CustomError("not logged in");
    return data;
  })
  .catch(error => {
    if (error instanceof CustomError) {/** custom handling of error*//}
    throw error
  })

如果这是你想要达到的目的,.name参数也很适合你:

export const ERROR_NOT_LOGGED_IN = "ERROR_NOT_LOGGED_IN";

axios
  .post(url, payload)
  .then(data => {
    if (!data.loggedIn) throw Error("not logged in").name=ERROR_NOT_LOGGED_IN ;
    return data;
  })
  .catch(error => {
    if (error.name === ERROR_NOT_LOGGED_IN) {/** custom handling of error*//}
    throw error
  })

Mohsen has a great answer above in ES6 that sets the name, but if you're using TypeScript or if you're living in the future where hopefully this proposal for public and private class fields has moved past stage 3 as a proposal and made it into stage 4 as part of ECMAScript/JavaScript then you might want to know this is then just a little bit shorter. Stage 3 is where browsers start implementing features, so if your browser supports it the code below just might work. (Tested in the new Edge browser v81 it seems to work fine). Be warned though this is an unstable feature at the moment and should be used cautiously and you should always check browser support on unstable features. This post is mainly for those future dwellers when browsers might support this. To check support check MDN and Can I use. It's currently got 66% support across the browser market which is getting there but not that great so if you really want to use it now and don't want to wait either use a transpiler like Babel or something like TypeScript.

类EOFError扩展错误{ name = " EOFError " } 抛出新的EOFError("Oops error ");

将此与一个无名错误进行比较,该错误在抛出时不会记录其名称。

类NamelessEOFError扩展错误{} 抛出新的NamelessEOFError("Oops 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

这段代码显示了这一切。

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