【问题标题】:Custom exception type自定义异常类型
【发布时间】:2010-10-02 15:17:19
【问题描述】:

我可以在 JavaScript 中为用户定义的异常定义自定义类型吗?如果是这样,我该怎么做?

【问题讨论】:

  • 当心。根据JavaScript in 10 Minutes,如果你抛出一个未装箱的值,你将不会得到堆栈跟踪。
  • exceptionsjs.com 提供了创建自定义异常的能力,并提供了一些缺失的异常,包括默认的 ArgumentException 和 NotImplemented。

标签: javascript exception


【解决方案1】:

使用throw 语句。

JavaScript 不关心异常类型是什么(就像 Java 一样)。 JavaScript 只是注意到,有一个异常,当您捕获它时,您可以“查看”异常“说”什么。

如果您必须抛出不同的异常类型,我建议使用包含异常字符串/对象的变量,即消息。在需要的地方使用“throw myException”并在 catch 中将捕获的异常与 myException 进行比较。

【讨论】:

    【解决方案2】:

    是的。你可以抛出任何你想要的东西:整数、字符串、对象等等。如果你想抛出一个对象,那么只需创建一个新对象,就像在其他情况下创建一个对象,然后抛出它。 Mozilla's Javascript reference 有几个例子。

    【讨论】:

      【解决方案3】:

      您可以实现自己的异常及其处理,例如:

      // 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");
          }
      }
      

      还有另一种语法用于捕获类型化异常,尽管这不适用于每个浏览器(例如在 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");
      }
      

      【讨论】:

      • MSN 网站带有关于条件捕获的警告:非标准 此功能是非标准的,不在标准轨道上。不要在面向 Web 的生产站点上使用它:它不适用于每个用户。实现之间也可能存在很大的不兼容性,并且未来的行为可能会发生变化。
      【解决方案4】:

      来自WebReference

      throw { 
        name:        "System Error", 
        level:       "Show Stopper", 
        message:     "Error detected. Please contact the system administrator.", 
        htmlMessage: "Error detected. Please contact the <a href=\"mailto:sysadmin@acme-widgets.com\">system administrator</a>.",
        toString:    function(){return this.name + ": " + this.message;} 
      }; 
      

      【讨论】:

      • @b.long 它在“JavaScript: The Good Parts”(IMO 巨著)中。此 Google 图书预览显示了以下部分:books.google.com/books?id=PXa2bby0oQ0C&pg=PA32&lpg=PA32
      • 添加一个 toString 方法将使它在 javascript 控制台中很好地显示。没有它显示为:未捕获的 # 与它显示为:未捕获的系统错误:检测到错误。请联系系统管理员。
      • 这将不允许您堆栈跟踪,除非您从 Error 继承
      • 如何在 catch 块中过滤以仅处理此自定义错误?
      • 这里不解释如何创建用户定义的异常。 -1 因为只有代码,没有解释。这些字段是强制性的吗?这些字段是可选的吗? toString() 函数对某些浏览器或某些其他 JavaScript 函数是否具有特定含义?等
      【解决方案5】:
      function MyError(message) {
       this.message = message;
      }
      
      MyError.prototype = new Error;
      

      这允许使用类似..

      try {
        something();
       } catch(e) {
        if(e instanceof MyError)
         doSomethingElse();
        else if(e instanceof Error)
         andNowForSomethingCompletelyDifferent();
      }
      

      【讨论】:

      • 即使您没有继承 Error 的原型,这个简短的示例难道不会以完全相同的方式工作吗?我不清楚在这个例子中你有什么收获。
      • 不,e instanceof Error 会是假的。
      • 确实如此。但由于e instanceof MyError 为真,else if(e instanceof Error) 语句将永远不会被评估。
      • 对,这只是这种 try/catch 样式如何工作的示例。 else if(e instanceof Error) 将是最后的收获。后面可能是一个简单的else(我没有包括在内)。有点像 switch 语句中的default:,但用于错误。
      【解决方案6】:

      您可以通过以下方式创建与本机 Error 的行为完全相同的自定义错误。这种技术目前仅适用于 Chrome 和 node.js。如果您不了解它的作用,我也不建议使用它

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

      因此我们得到

      /**
       * 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)
      

      那么你可以这样使用它:

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

      并像使用Error 一样使用NotImplementedError

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

      它的行为是预期的:

      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!
      

      请注意,error.stack 工作绝对正确,不会包含 NotImplementedError 构造函数调用(感谢 v8 的 Error.captureStackTrace())。

      注意。有丑陋的eval()。使用它的唯一原因是获得正确的err.constructor.name。如果你不需要它,你可以把一切都简化一下。

      【讨论】:

      【解决方案7】:

      我经常使用原型继承的方法。覆盖 toString() 为您提供了这样的优势,即像 Firebug 这样的工具会将实际信息而不是 [object Object] 记录到控制台以用于未捕获的异常。

      使用instanceof 来确定异常的类型。

      main.js

      // just an exemplary namespace
      var ns = ns || {};
      
      // include JavaScript of the following
      // source files here (e.g. by concatenation)
      
      var someId = 42;
      throw new ns.DuplicateIdException('Another item with ID ' +
          someId + ' has been created');
      // Firebug console:
      // uncaught exception: [Duplicate ID] Another item with ID 42 has been created
      

      Exception.js

      ns.Exception = function() {
      }
      
      /**
       * Form a string of relevant information.
       *
       * When providing this method, tools like Firebug show the returned 
       * string instead of [object Object] for uncaught exceptions.
       *
       * @return {String} information about the exception
       */
      ns.Exception.prototype.toString = function() {
          var name = this.name || 'unknown';
          var message = this.message || 'no description';
          return '[' + name + '] ' + message;
      };
      

      DuplicateIdException.js

      ns.DuplicateIdException = function(message) {
          this.name = 'Duplicate ID';
          this.message = message;
      };
      
      ns.DuplicateIdException.prototype = new ns.Exception();
      

      【讨论】:

        【解决方案8】:
        //create error object
        var error = new Object();
        error.reason="some reason!";
        
        //business function
        function exception(){
            try{
                throw error;
            }catch(err){
                err.reason;
            }
        }
        

        现在我们设置添加原因或我们想要的任何属性到错误对象并检索它。通过使错误更合理。

        【讨论】:

          【解决方案9】:

          您应该创建一个在原型上继承自 Error 的自定义异常。例如:

          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;
          

          这基本上是上面disfated 发布的内容的简化版本,增强了堆栈跟踪在 Firefox 和其他浏览器上的工作。它满足他发布的相同测试:

          用法:

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

          它的行为是预期的:

          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!
          

          【讨论】:

          • 更加灵活和可扩展。
          【解决方案10】:

          请参阅 MDN 中的 this example

          如果需要定义多个Errors(测试代码here!):

          function createErrorType(name, initFunction) {
              function E(message) {
                  this.message = message;
                  if (Error.captureStackTrace)
                      Error.captureStackTrace(this, this.constructor);
                  else
                      this.stack = (new Error()).stack;
                  initFunction && initFunction.apply(this, arguments);
              }
              E.prototype = Object.create(Error.prototype);
              E.prototype.name = name;
              E.prototype.constructor = E;
              return E;
          }
          var InvalidStateError = createErrorType(
              'InvalidStateError',
              function (invalidState, acceptedStates) {
                  this.message = 'The state ' + invalidState + ' is invalid. Expected ' + acceptedStates + '.';
              });
          
          var error = new InvalidStateError('foo', 'bar or baz');
          function assert(condition) { if (!condition) throw new Error(); }
          assert(error.message);
          assert(error instanceof InvalidStateError);  
          assert(error instanceof Error); 
          assert(error.name == 'InvalidStateError');
          assert(error.stack);
          error.message;
          

          代码大部分抄自:What's a good way to extend Error in JavaScript?

          【讨论】:

            【解决方案11】:

            asselin 答案的替代方案,用于 ES2015 类

            class InvalidArgumentException extends Error {
                constructor(message) {
                    super();
                    Error.captureStackTrace(this, this.constructor);
                    this.name = "InvalidArgumentException";
                    this.message = message;
                }
            }
            

            【讨论】:

              【解决方案12】:

              简而言之:

              选项 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);
              
              • 如果您使用的是纯 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;
                }
                
              • 替代方案:使用Classtrophobic 框架

              说明:

              为什么使用 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的官方文档来看,你cannot extend any built-in JavaScript classes比如DateArrayDOM或者Error

              这里描述了这个问题:

              其他 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 问题,还可以保留常规错误console.log

              console.log(new CustomError('test'));
              // output:
              // Error: test
              //     at CustomError (<anonymous>:2:32)
              //     at <anonymous>:1:5
              

              【讨论】:

                【解决方案13】:

                ES6

                有了新的 class 和 extend 关键字,现在变得更容易了:

                class CustomError extends Error {
                  constructor(message) {
                    super(message);
                    //something
                  }
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2021-08-24
                  • 2012-03-26
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-05-07
                  • 1970-01-01
                  • 2017-03-29
                  相关资源
                  最近更新 更多