我可以在JavaScript中为用户定义的异常定义自定义类型吗?如果可以的话,我该怎么做?
来自WebReference:
throw { name: "System Error", level: "Show Stopper", message: "Error detected. Please contact the system administrator.", htmlMessage: "Error detected. Please contact the system administrator.", toString: function(){return this.name + ": " + this.message;} };
您应该创建一个原型继承自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!
您可以实现自己的异常及其处理,例如:
// 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"); }
是.你可以扔任何你想要的东西:整数,字符串,对象,等等.如果要抛出一个对象,那么只需创建一个新对象,就像在其他情况下创建一个对象一样,然后抛出它.Mozilla的Javascript参考有几个例子.
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(); }
简而言之:
如果您使用的是没有转发器的ES6 :
class CustomError extends Error { /* ... */}
请参阅使用ES6语法扩展Javascript中的错误,了解当前的最佳做法
如果您使用的是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
事实上,从巴贝尔的官方文档,你不能扩展任何内置的JavaScript类如Date,Array,DOM或Error.
Date
Array
DOM
Error
这个问题在这里描述:
Native扩展了HTMLELement,Array等
由类,数组,对象,字符串或错误等基类型扩展的类的对象不是此类的实例
其他SO答案怎么样?
所有给出的答案都可以解决instanceof问题,但是您会丢失常规错误console.log:
instanceof
console.log
console.log(new CustomError('test')); // output: // CustomError {name: "MyError", message: "test", stack: "Error? at CustomError (:4:19)? at :1:5"}
而使用上述方法,不仅可以解决instanceof问题,还可以保留常规错误console.log:
console.log(new CustomError('test')); // output: // Error: test // at CustomError (:2:32) // at :1:5
以下是如何创建与本机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:
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()).
error.stack
Error.captureStackTrace()
注意.有丑陋的eval().使用它的唯一原因是得到正确的err.constructor.name.如果您不需要它,您可以稍微简化一切.
eval()
err.constructor.name
我经常使用原型继承的方法.覆盖toString()为您提供了一个优势,即像Firebug这样的工具会记录实际信息而不是[object Object]控制台中的未捕获异常.
toString()
[object Object]
使用instanceof以确定异常的类型.
// 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
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; };
ns.DuplicateIdException = function(message) { this.name = 'Duplicate ID'; this.message = message; }; ns.DuplicateIdException.prototype = new ns.Exception();
ES6
使用新的class和extend关键字,现在变得更加容易:
class CustomError extends Error { constructor(message) { super(message); //something } }
使用throw语句.
JavaScript并不关心异常类型是什么(就像Java那样).JavaScript只是注意到,有一个例外,当你抓住它时,你可以"看"异常"说".
如果您必须抛出不同的异常类型,我建议使用包含异常的字符串/对象的变量,即消息.在需要的地方使用"throw myException"并在catch中将捕获的异常与myException进行比较.