Custom exception type

前端 未结 13 1715
生来不讨喜
生来不讨喜 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 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.

提交回复
热议问题