Custom exception type

前端 未结 13 1698
生来不讨喜
生来不讨喜 2020-11-28 17:40

Can I define custom types for user-defined exceptions in JavaScript? If so, how would I do it?

相关标签:
13条回答
  • 2020-11-28 17:58

    An alternative to the answer of asselin for use with ES2015 classes

    class InvalidArgumentException extends Error {
        constructor(message) {
            super();
            Error.captureStackTrace(this, this.constructor);
            this.name = "InvalidArgumentException";
            this.message = message;
        }
    }
    
    0 讨论(0)
  • 2020-11-28 17:58
    //create error object
    var error = new Object();
    error.reason="some reason!";
    
    //business function
    function exception(){
        try{
            throw error;
        }catch(err){
            err.reason;
        }
    }
    

    Now we set add the reason or whatever properties we want to the error object and retrieve it. By making the error more reasonable.

    0 讨论(0)
  • 2020-11-28 18:00

    You could implement your own exceptions and their handling for example like here:

    // define exceptions "classes" 
    function NotNumberException() {}
    function NotPositiveNumberException() {}
    
    // try some code
    try {
        // some function/code that can throw
        if (isNaN(value))
            throw new NotNumberException();
        else
        if (value < 0)
            throw new NotPositiveNumberException();
    }
    catch (e) {
        if (e instanceof NotNumberException) {
            alert("not a number");
        }
        else
        if (e instanceof NotPositiveNumberException) {
            alert("not a positive number");
        }
    }
    

    There is another syntax for catching a typed exception, although this won't work in every browser (for example not in IE):

    // define exceptions "classes" 
    function NotNumberException() {}
    function NotPositiveNumberException() {}
    
    // try some code
    try {
        // some function/code that can throw
        if (isNaN(value))
            throw new NotNumberException();
        else
        if (value < 0)
            throw new NotPositiveNumberException();
    }
    catch (e if e instanceof NotNumberException) {
        alert("not a number");
    }
    catch (e if e instanceof NotPositiveNumberException) {
        alert("not a positive number");
    }
    
    0 讨论(0)
  • 2020-11-28 18:03

    In short:

    • If you are using ES6 without transpilers:

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

      See Extending Error in Javascript with ES6 syntax for what's the current best practice

    • If you are using Babel transpiler:

    Option 1: use babel-plugin-transform-builtin-extend

    Option 2: do it yourself (inspired from that same library)

        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

    Explanation:

    Why extending the Error class using ES6 and Babel is a problem?

    Because an instance of CustomError is not anymore recognized as such.

    class CustomError extends Error {}
    console.log(new CustomError('test') instanceof Error);// true
    console.log(new CustomError('test') instanceof CustomError);// false
    

    In fact, from the official documentation of Babel, you cannot extend any built-in JavaScript classes such as Date, Array, DOM or Error.

    The issue is described here:

    • Native extends breaks HTMLELement, Array, and others
    • an object of The class which is extends by base type like Array,Number,Object,String or Error is not instanceof this class

    What about the other SO answers?

    All the given answers fix the instanceof issue but you lose the regular error console.log:

    console.log(new CustomError('test'));
    // output:
    // CustomError {name: "MyError", message: "test", stack: "Error↵    at CustomError (<anonymous>:4:19)↵    at <anonymous>:1:5"}
    

    Whereas using the method mentioned above, not only you fix the instanceof issue but you also keep the regular error console.log:

    console.log(new CustomError('test'));
    // output:
    // Error: test
    //     at CustomError (<anonymous>:2:32)
    //     at <anonymous>:1:5
    
    0 讨论(0)
  • 2020-11-28 18:06

    Here is how you can create custom errors with completely identical to native Error's behaviour. This technique works only in Chrome and node.js for now. I also wouldn't recommend to use it if you don't understand what it does.

    Error.createCustromConstructor = (function() {
    
        function define(obj, prop, value) {
            Object.defineProperty(obj, prop, {
                value: value,
                configurable: true,
                enumerable: false,
                writable: true
            });
        }
    
        return function(name, init, proto) {
            var CustomError;
            proto = proto || {};
            function build(message) {
                var self = this instanceof CustomError
                    ? this
                    : Object.create(CustomError.prototype);
                Error.apply(self, arguments);
                Error.captureStackTrace(self, CustomError);
                if (message != undefined) {
                    define(self, 'message', String(message));
                }
                define(self, 'arguments', undefined);
                define(self, 'type', undefined);
                if (typeof init == 'function') {
                    init.apply(self, arguments);
                }
                return self;
            }
            eval('CustomError = function ' + name + '() {' +
                'return build.apply(this, arguments); }');
            CustomError.prototype = Object.create(Error.prototype);
            define(CustomError.prototype, 'constructor', CustomError);
            for (var key in proto) {
                define(CustomError.prototype, key, proto[key]);
            }
            Object.defineProperty(CustomError.prototype, 'name', { value: name });
            return CustomError;
        }
    
    })();
    

    As a reasult we get

    /**
     * name   The name of the constructor name
     * init   User-defined initialization function
     * proto  It's enumerable members will be added to 
     *        prototype of created constructor
     **/
    Error.createCustromConstructor = function(name, init, proto)
    

    Then you can use it like this:

    var NotImplementedError = Error.createCustromConstructor('NotImplementedError');
    

    And use NotImplementedError as you would Error:

    throw new NotImplementedError();
    var err = new NotImplementedError();
    var err = NotImplementedError('Not yet...');
    

    And it will behave is expected:

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

    Note, that error.stack works absolutle correct and won't include NotImplementedError constructor call (thanks to v8's Error.captureStackTrace()).

    Note. There is ugly eval(). The only reason it is used is to get correct err.constructor.name. If you don't need it, you can a bit simplify everything.

    0 讨论(0)
  • 2020-11-28 18:07

    You should create a custom exception that prototypically inherits from Error. For example:

    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;
    

    This is basically a simplified version of what disfated posted above with the enhancement that stack traces work on Firefox and other browsers. It satisfies the same tests that he posted:

    Usage:

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

    And it will behave is expected:

    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!
    
    0 讨论(0)
提交回复
热议问题