自定义例外类型


224

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


3
谨防。根据JavaScript在10分钟内的说明,如果抛出未装箱的值,则不会获得堆栈跟踪。
Janus Troelsen

exceptionsjs.com提供了创建自定义异常的功能,并提供了一些缺少的异常,包括默认情况下的ArgumentException和NotImplemented。
Steven Wexler 2014年

Answers:


232

来自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;} 
}; 

7
@ b.long在“ JavaScript:The Good Parts”(IMO精装本)中。此Google图书预览显示以下部分:books.google.com/books?
id=PXa2bby0oQ0C&pg=PA32&lpg=PA32

11
添加toString方法将使其在javascript控制台中很好地显示。如果没有,则显示为:Untoaught#<Object>,带有toString,显示为:Uncaught系统错误:检测到错误。请联系系统管理员。
JDC

11
除非您从Error
Luke H

如何在catch块中进行过滤以仅处理此自定义错误?
Overdrivr

@overdrivr之类的东西,例如⦃ catch (e) { if (e instanceof TypeError) { … } else { throw e; } }catch (e) { switch (e.constructor) { case TypeError: …; break; default: throw e; }⦃。
sam boosalis,

92

您应该创建一个典型地从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;

这基本上是上面发布的内容的简化版本,具有堆栈跟踪功能的增强功能,该功能可在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!

80

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

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

2
MSN网站带有有关条件捕获的警告:非标准此功能是非标准的,不在标准轨道上。不要在面向Web的生产站点上使用它:它不适用于每个用户。实现之间也可能存在很大的不兼容性,并且将来的行为可能会改变。
劳伦斯·多尔2014年

40

是。您可以抛出任何想要的东西:整数,字符串,对象等等。如果要抛出一个对象,则只需创建一个新对象,就像在其他情况下创建一个对象然后将其抛出一样。Mozilla的Javascript参考包含几个示例。


26
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的原型,这个简短的示例也不会以完全相同的方式工作吗?在这个例子中,我尚不清楚什么能使您受益。
EleventyOne

1
不,e instanceof Error会是错误的。
摩根ARR艾伦

确实。但是既然e instanceof MyError是真的,那么else if(e instanceof Error)就永远不会评估该语句。
EleventyOne

正确,这只是这种try / catch样式如何工作的示例。哪里else if(e instanceof Error)是最后的收获。可能后面跟着一个简单的else(我未包括在内)。类似于default:switch语句中的,但是有错误。
Morgan ARR艾伦

15

简而言之:

选项1:使用babel-plugin-transform-b​​uiltin-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

事实上,从巴贝尔的官方文档,你不能扩展任何内置的JavaScript类DateArrayDOMError

问题描述如下:

那么其他答案呢?

所有给出的答案都解决了该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

11

这是创建与本地用户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');

并使用NotImplementedError,你会Error

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。如果您不需要它,则可以简化所有步骤。


2
Error.apply(self, arguments)指定为无效。我建议复制堆栈跟踪,而不是跨浏览器兼容。
Kornel 2014年

11

我经常使用具有原型继承的方法。覆盖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

ES6

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

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

7

使用throw语句。

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

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


1

请参阅MDN中的此示例

如果您需要定义多个错误(请在此处测试代码!):

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;

代码主要从以下地方复制:扩展JavaScript错误的一种好方法是什么?


1

到的答案的替代ASSELIN与ES2015类使用

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

1
//create error object
var error = new Object();
error.reason="some reason!";

//business function
function exception(){
    try{
        throw error;
    }catch(err){
        err.reason;
    }
}

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.