如何在JavaScript中创build自定义错误?

出于某种原因,它看起来像构造函数代表不起作用在下面的代码片段:

function NotImplementedError() { Error.apply(this, arguments); } NotImplementedError.prototype = new Error(); var nie = new NotImplementedError("some message"); console.log("The message is: '"+nie.message+"'") 

运行这个The message is: '' 。 任何想法,为什么,或者如果有更好的方式来创build一个新的Error子类? apply到我不知道的本地Error构造函数是否有问题?

更新你的代码,把你的原型分配给Error.prototype,instanceof和你的断言工作。

 function NotImplementedError(message) { this.name = "NotImplementedError"; this.message = (message || ""); } NotImplementedError.prototype = Error.prototype; 

不过,我只是抛出自己的对象,只是检查名称属性。

 throw {name : "NotImplementedError", message : "too lazy to implement"}; 

根据评论编辑

看了评论,并试图记住为什么我将原型分配给Error.prototype而不是像Nicholas Zakas在他的文章中所做的new Error() ,我创build了一个jsFiddle ,代码如下:

 function NotImplementedError(message) { this.name = "NotImplementedError"; this.message = (message || ""); } NotImplementedError.prototype = Error.prototype; function NotImplementedError2(message) { this.message = (message || ""); } NotImplementedError2.prototype = new Error(); try { var e = new NotImplementedError("NotImplementedError message"); throw e; } catch (ex1) { console.log(ex1.stack); console.log("ex1 instanceof NotImplementedError = " + (ex1 instanceof NotImplementedError)); console.log("ex1 instanceof Error = " + (ex1 instanceof Error)); console.log("ex1.name = " + ex1.name); console.log("ex1.message = " + ex1.message); } try { var e = new NotImplementedError2("NotImplementedError2 message"); throw e; } catch (ex1) { console.log(ex1.stack); console.log("ex1 instanceof NotImplementedError2 = " + (ex1 instanceof NotImplementedError2)); console.log("ex1 instanceof Error = " + (ex1 instanceof Error)); console.log("ex1.name = " + ex1.name); console.log("ex1.message = " + ex1.message); } 

所有上述的答案是可怕的 – 真的。 即使是与107 ups! 真正的答案在这里家伙:

inheritanceError对象 – 消息属性在哪里?

TL; DR:

答:原因message没有被设置是Error是一个函数,返回一个新的错误对象,并不以任何方式操纵this

B.这样做的方法是从构造函数中返回apply的结果,并以通常复杂的javascripty方式设置原型:

 function MyError() { var temp = Error.apply(this, arguments); temp.name = this.name = 'MyError'; this.message = temp.message; if(Object.defineProperty) { // getter for more optimizy goodness /*this.stack = */Object.defineProperty(this, 'stack', { get: function() { return temp.stack }, configurable: true // so you can change it if you want }) } else { this.stack = temp.stack } } //inherit prototype using ECMAScript 5 (IE 9+) MyError.prototype = Object.create(Error.prototype, { constructor: { value: MyError, writable: true, configurable: true } }); var myError = new MyError("message"); console.log("The message is: '" + myError.message + "'"); // The message is: 'message' console.log(myError instanceof Error); // true console.log(myError instanceof MyError); // true console.log(myError.toString()); // MyError: message console.log(myError.stack); // MyError: message \n // <stack trace ...> //for EMCAScript 4 or ealier (IE 8 or ealier), inherit prototype this way instead of above code: /* var IntermediateInheritor = function() {}; IntermediateInheritor.prototype = Error.prototype; MyError.prototype = new IntermediateInheritor(); */ 

如果有人对如何创build自定义错误获取堆栈跟踪感到好奇:

 function CustomError(message) { this.name = 'CustomError'; this.message = message || ''; var error = new Error(this.message); error.name = this.name; this.stack = error.stack; } CustomError.prototype = Object.create(Error.prototype); try { throw new CustomError('foobar'); } catch (e) { console.log('name:', e.name); console.log('message:', e.message); console.log('stack:', e.stack); } 

标准的这部分可以解释为什么Error.apply调用不初始化对象:

15.11.1作为函数调用的错误构造函数

当Error作为一个函数而不是构造函数被调用时,它会创build并初始化一个新的Error对象。 因此,调用Error(…)的函数等同于具有相同参数的对象创buildexpression式new Error(…)。

在这种情况下, Error函数可能会确定它没有被作为构造函数调用,所以它返回一个新的Error实例,而不是初始化this对象。

使用下面的代码testing似乎certificate这实际上是发生了什么事情:

 function NotImplementedError() { var returned = Error.apply(this, arguments); console.log("returned.message = '" + returned.message + "'"); console.log("this.message = '" + this.message + "'"); } NotImplementedError.prototype = new Error(); var nie = new NotImplementedError("some message"); 

运行时会生成以下输出:

 returned.message = 'some message' this.message = '' 

我有类似的问题。 我的错误需要是ErrorNotImplemented一个instanceof ,并且还需要在控制台中产生连贯的回溯。

我的解决scheme

 var NotImplemented = (function() { var NotImplemented, err; NotImplemented = (function() { function NotImplemented(message) { var err; err = new Error(message); err.name = "NotImplemented"; this.message = err.message; if (err.stack) this.stack = err.stack; } return NotImplemented; })(); err = new Error(); err.name = "NotImplemented"; NotImplemented.prototype = err; return NotImplemented; }).call(this); // TEST: console.log("instanceof Error: " + (new NotImplemented() instanceof Error)); console.log("instanceof NotImplemented: " + (new NotImplemented() instanceofNotImplemented)); console.log("message: "+(new NotImplemented('I was too busy').message)); throw new NotImplemented("just didn't feel like it"); 

使用node.js运行的结果:

 instanceof Error: true instanceof NotImplemented: true message: I was too busy /private/tmp/t.js:24 throw new NotImplemented("just didn't feel like it"); ^ NotImplemented: just didn't feel like it at Error.NotImplemented (/Users/colin/projects/gems/jax/t.js:6:13) at Object.<anonymous> (/Users/colin/projects/gems/jax/t.js:24:7) at Module._compile (module.js:449:26) at Object.Module._extensions..js (module.js:467:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Module.runMain (module.js:487:10) at process.startup.processNextTick.process._tickCallback (node.js:244:9) 

该错误通过了我所有的3个条件,虽然stack属性是非标准的, 但是在大多数较新的浏览器中都支持,在我的情况下是可以接受的。

 function InvalidValueError(value, type) { this.message = "Expected `" + type.name + "`: " + value; var error = new Error(this.message); this.stack = error.stack; } InvalidValueError.prototype = new Error(); InvalidValueError.prototype.name = InvalidValueError.name; InvalidValueError.prototype.constructor = InvalidValueError; 

我只需要实现这样的东西,发现堆栈丢失了我自己的错误实现。 我必须做的是创build一个虚拟的错误,并从中检索堆栈:

 My.Error = function (message, innerException) { var err = new Error(); this.stack = err.stack; // IMPORTANT! this.name = "Error"; this.message = message; this.innerException = innerException; } My.Error.prototype = new Error(); My.Error.prototype.constructor = My.Error; My.Error.prototype.toString = function (includeStackTrace) { var msg = this.message; var e = this.innerException; while (e) { msg += " The details are:\n" + e.message; e = e.innerException; } if (includeStackTrace) { msg += "\n\nStack Trace:\n\n" + this.stack; } return msg; } 

对于Joyent来说,你不应该把堆栈属性(我在这里给出的许多答案中看到)弄乱,因为这会对性能产生负面影响。 这是他们说的:

一般来说,不要惹这个事情。 甚至不要增加它。 如果有人真的读取了这个属性,V8只会计算它,这可以显着提高可处理错误的性能。 如果你阅读这个财产来增加它,即使你的调用者不需要这个堆栈,你也会付出代价。

我喜欢并且想提一下他们关于包装原始错误的想法,这个错误是堆栈传递的一个很好的替代品。

所以这里是我如何创build一个自定义的错误,考虑到上面提到的:

es5版本:

 function RError(options) { options = options || {}; // eslint-disable-line no-param-reassign this.name = options.name; this.message = options.message; this.cause = options.cause; // capture stack (this property is supposed to be treated as private) this._err = new Error(); // create an iterable chain this.chain = this.cause ? [this].concat(this.cause.chain) : [this]; } RError.prototype = Object.create(Error.prototype, { constructor: { value: RError, writable: true, configurable: true } }); Object.defineProperty(RError.prototype, 'stack', { get: function stack() { return this.name + ': ' + this.message + '\n' + this._err.stack.split('\n').slice(2).join('\n'); } }); Object.defineProperty(RError.prototype, 'why', { get: function why() { var _why = this.name + ': ' + this.message; for (var i = 1; i < this.chain.length; i++) { var e = this.chain[i]; _why += ' <- ' + e.name + ': ' + e.message; } return _why; } }); // usage function fail() { throw new RError({ name: 'BAR', message: 'I messed up.' }); } function failFurther() { try { fail(); } catch (err) { throw new RError({ name: 'FOO', message: 'Something went wrong.', cause: err }); } } try { failFurther(); } catch (err) { console.error(err.why); console.error(err.stack); console.error(err.cause.stack); } 

我使用构造函数模式来创build新的错误对象。 我定义了原型链,比如一个Error实例。 请参阅MDN 错误构造函数参考。

你可以在这个要点上检查这个片段。

实施

 // Creates user-defined exceptions var CustomError = (function() { 'use strict'; //constructor function CustomError() { //enforces 'new' instance if (!(this instanceof CustomError)) { return new CustomError(arguments); } var error, //handles the arguments object when is passed by enforcing a 'new' instance args = Array.apply(null, typeof arguments[0] === 'object' ? arguments[0] : arguments), message = args.shift() || 'An exception has occurred'; //builds the message with multiple arguments if (~message.indexOf('}')) { args.forEach(function(arg, i) { message = message.replace(RegExp('\\{' + i + '}', 'g'), arg); }); } //gets the exception stack error = new Error(message); //access to CustomError.prototype.name error.name = this.name; //set the properties of the instance //in order to resemble an Error instance Object.defineProperties(this, { stack: { enumerable: false, get: function() { return error.stack; } }, message: { enumerable: false, value: message } }); } // Creates the prototype and prevents the direct reference to Error.prototype; // Not used new Error() here because an exception would be raised here, // but we need to raise the exception when CustomError instance is created. CustomError.prototype = Object.create(Error.prototype, { //fixes the link to the constructor (ES5) constructor: setDescriptor(CustomError), name: setDescriptor('JSU Error') }); function setDescriptor(value) { return { configurable: false, enumerable: false, writable: false, value: value }; } //returns the constructor return CustomError; }()); 

用法

CustomError构造函数可以接收许多参数来构build消息,例如

 var err1 = new CustomError("The url of file is required"), err2 = new CustomError("Invalid Date: {0}", +"date"), err3 = new CustomError("The length must be greater than {0}", 4), err4 = new CustomError("Properties .{0} and .{1} don't exist", "p1", "p2"); throw err4; 

这是自定义错误的样子:

自定义错误原型链

构造函数需要像工厂方法,并返回你想要的。 如果您需要其他方法/属性,可以在返回对象之前将它们添加到对象中。

 function NotImplementedError(message) { return new Error("Not implemented", message); } x = new NotImplementedError(); 

虽然我不知道为什么你需要这样做。 为什么不使用new Error... ? 自定义exception在JavaScript中并不真正添加(或者可能是任何非types化的语言)。

这在Cesium DeveloperError中很好地实现:

  • 文件
  • 资源

它的简化forms是:

 var NotImplementedError = function(message) { this.name = 'NotImplementedError'; this.message = message; this.stack = (new Error()).stack; } // Later on... throw new NotImplementedError(); 

在ES2015中,您可以使用class干净地做到这一点:

 class NotImplemented extends Error { constructor(message = "", ...args) { super(message, ...args); this.message = message + " has not yet been implemented."; } } 

这不会修改全局Error原型,允许您自定义messagename和其他属性,并正确捕获堆栈。 这也是非常可读的。

当然,如果您的代码将在旧版浏览器上运行,您可能需要使用像babel这样的工具。

以不能使用instanceof为代价,下面的代码保留了原始的堆栈跟踪,并没有使用任何非标准的技巧。

 // the function itself var fixError = function(err, name) { err.name = name; return err; } // using the function try { throw fixError(new Error('custom error message'), 'CustomError'); } catch (e) { if (e.name == 'CustomError') console.log('Wee! Custom Error! Msg:', e.message); else throw e; // unhandled. let it propagate upwards the call stack } 

另一个select,可能不适用于所有的环境.Aastast保证它在nodejs 0.8中工作。这种方法使用非标准的方式来修改内部原型道具

 function myError(msg){ var e = new Error(msg); _this = this; _this.__proto__.__proto__ = e; } 

如果您使用Node / Chrome。 下面的代码片段将得到你的扩展符合下列要求。

  • err instanceof Error
  • err instanceof CustomErrorType
  • console.log()在用消息创build时返回[CustomErrorType]
  • console.log()在没有消息的情况下创build时返回[CustomErrorType: message]
  • 抛出/堆栈提供了在创build错误时的信息。
  • 在Node.JS和Chrome中最佳运行。
  • 在Chrome,Safari,Firefox和IE 8+中都会传入instanceof检查,但在Chrome / Safari之外不会有有效的堆栈。 我可以这样做,因为我可以在Chrome中debugging,但需要特定错误types的代码仍然可以跨浏览器。 如果你只需要Node,你可以很容易地删除if语句,你很好

片段

 var CustomErrorType = function(message) { if (Object.defineProperty) { Object.defineProperty(this, "message", { value : message || "", enumerable : false }); } else { this.message = message; } if (Error.captureStackTrace) { Error.captureStackTrace(this, CustomErrorType); } } CustomErrorType.prototype = new Error(); CustomErrorType.prototype.name = "CustomErrorType"; 

用法

 var err = new CustomErrorType("foo"); 

产量

 var err = new CustomErrorType("foo"); console.log(err); console.log(err.stack); [CustomErrorType: foo] CustomErrorType: foo at Object.<anonymous> (/errorTest.js:27:12) at Module._compile (module.js:456:26) at Object.Module._extensions..js (module.js:474:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Function.Module.runMain (module.js:497:10) at startup (node.js:119:16) at node.js:906:3 /errorTest.js:30 throw err; ^ CustomErrorType: foo at Object.<anonymous> (/errorTest.js:27:12) at Module._compile (module.js:456:26) at Object.Module._extensions..js (module.js:474:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Function.Module.runMain (module.js:497:10) at startup (node.js:119:16) at node.js:906:3 

为用户定义的错误types的每个实例尝试一个新的原型对象。 它允许instanceof检查行为像往常一样加上types和消息正确报告在Firefox和V8(Chome,nodejs)。

 function NotImplementedError(message){ if(NotImplementedError.innercall===undefined){ NotImplementedError.innercall = true; NotImplementedError.prototype = new Error(message); NotImplementedError.prototype.name = "NotImplementedError"; NotImplementedError.prototype.constructor = NotImplementedError; return new NotImplementedError(message); } delete NotImplementedError.innercall; } 

请注意,在其他正确的堆栈之前会有一个额外的入口。

MDN有一个很好的例子 :

 try { throw new Error('Whoops!'); } catch (e) { console.log(e.name + ': ' + e.message); } 

更简单的方法 你可以让你的对象inheritanceError对象。 例:

 function NotImplementError(message) { this.message = message; Error.call(); Error.call(message); } 

我们正在做的是使用调用Error类构造函数的call()方法,这与在其他面向对象语言中实现类inheritance基本相同。