自定义异常类型

Posted

技术标签:

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

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

【问题讨论】:

当心。根据JavaScript in 10 Minutes,如果你抛出一个未装箱的值,你将不会得到堆栈跟踪。 exceptionsjs.com 提供了创建自定义异常的能力,并提供了一些缺失的异常,包括默认的 ArgumentException 和 NotImplemented。 【参考方案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。如果你不需要它,你可以把一切都简化一下。

【讨论】:

Error.apply(self, arguments) 是 specified not to work。我建议copying the stack trace instead 是跨浏览器兼容的。【参考方案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】:

简而言之:

如果您使用的是 ES6没有转译器

class CustomError extends Error  /* ... */

请参阅 Extending Error in Javascript with ES6 syntax 了解当前的最佳做法

如果您使用的是 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);

如果您使用的是纯 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

这里描述了这个问题:

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

其他 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
  

【讨论】:

以上是关于自定义异常类型的主要内容,如果未能解决你的问题,请参考以下文章

springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如

@ExceptionHandler 用于自定义 ResponseEntityExceptionHandler 中的 Spring 异常未调用类型异常

在 laravel 中处理异常而不在异常处理程序的渲染方法中进行类型检查异常并且不定义自定义异常?

Java中的自定义异常捕获方式

自定义异常

Object-异常声明及捕获-自定义异常