我在Javascript中定义自定义错误对象时注意到一个奇怪的行为:
function MyError(msg) {
Error.call(this, msg);
this.name = "MyError";
}
MyError.prototype.__proto__ = Error.prototype;
var error = new Error("message");
error.message; // "message"
var myError = new MyError("message");
myError instanceof Error; // true
myError.message; // "" !
为什么new Error("message")
设置message
属性,而Error.call(this, msg);
不设置?当然,我可以在MyError
构造函数中定义this.message = msg
,但我不太明白为什么它一开始就没有设置。
A。Raynos说,message
没有被设置的原因是Error
是一个返回新Error对象的函数,它不会以任何方式操作this
。
B。做到这一点的方法是在this
上设置构造函数的应用结果,并以通常复杂的javascripty方式设置原型:
function MyError() {
var tmp = Error.apply(this, arguments)
tmp.name = this.name = 'MyError'
this.message = tmp.message
// instead of this.stack = ..., a getter for more optimizy goodness
Object.defineProperty(this, 'stack', {
get: function () {
return tmp.stack
}
})
return this
}
var IntermediateInheritor = function () {}
IntermediateInheritor.prototype = Error.prototype
MyError.prototype = new IntermediateInheritor()
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 ...>
在这一点上,这种方法唯一的问题是
- 除了
stack
和message
之外的属性不包括在MyError
中,并且 - stacktrace有一个额外的行,这不是真正必要的
第一个问题可以通过使用以下答案中的技巧迭代错误的所有不可枚举属性来解决:有可能获得对象的不可枚举继承属性名吗?,但ie<9.第二个问题可以通过删除堆栈跟踪中的那一行来解决,但我不知道如何安全地做到这一点(也许只是删除e.stack.toString()的第二行??)。
更新
我创建了一个继承库来执行此操作^https://github.com/fresheneesz/proto
function MyError(msg) {
var err = Error.call(this, msg);
err.name = "MyError";
return err;
}
Error
不操作this
,它创建了一个新的错误对象并返回。这就是为什么Error("foo")
在没有new
关键字的情况下也能正常工作的原因。
请注意,这是特定于实现的,v8(chrome&node.js)的行为如下。
MyError.prototype.__proto__ = Error.prototype;
也是一种不好的做法。使用
MyError.prototype = Object.create(Error.prototype, {
constructor: { value: MyError }
});
在Node.js中,您可以创建如下自定义错误:
var util = require('util');
function MyError(message) {
this.message = message;
Error.captureStackTrace(this, MyError);
}
util.inherits(MyError, Error);
MyError.prototype.name = 'MyError';
请参阅节点文档中的captureStackTrace
在ES6中这样做有什么问题?
class MyError extends Error {
constructor(message) {
super(message);
// Maintains proper stack trace (only on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, MyError);
}
this.appcode= 123; // can add custom props
}
}
您可以使用Error.captureStackTrace过滤掉堆栈跟踪中不需要的行。
function MyError() {
var tmp = Error.apply(this, arguments);
tmp.name = this.name = 'MyError';
this.message = tmp.message;
/*this.stack = */Object.defineProperty(this, 'stack', { // getter for more optimizy goodness
get: function() {
return tmp.stack;
}
});
Error.captureStackTrace(this, MyError); // showing stack trace up to instantiation of Error excluding it.
return this;
}
var IntermediateInheritor = function() {},
IntermediateInheritor.prototype = Error.prototype;
MyError.prototype = new IntermediateInheritor();
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 ...>
另一种方法是使新的错误实例成为this
的原型,这样你就不必知道要复制什么属性,这就绕过了BT在回答结束时谈到的问题。
function MyError() {
if (this === undefined) {
throw TypeError("MyError must be called as a constructor");
}
let newErr = Error.apply(undefined, arguments);
Object.setPrototypeOf(newErr, MyError.prototype);
Object.setPrototypeOf(this, newErr);
}
MyError.prototype = Object.create(Error.prototype);
let me = new MyError("A terrible thing happened");
console.log(me instanceof MyError); // true
console.log(me instanceof Error); // true
console.log(me.message); // A terrible thing happened
就我的钱而言,它更整洁了但是请注意,Object.setPrototypeOf()
(或支持它的不符合ES6的实现上的object.__proto__ =
)可能非常慢,所以如果您在黄金路径上使用这些错误,那么您可能不想这样做。
我非常喜欢制作可重复使用的.js文件,这些文件几乎放在我参与的任何项目中。当我有时间的时候,它会变成一个模块。
对于我的错误,我创建了一个exceptions.js
文件并将其添加到我的文件中。
下面是这个文件中的代码示例:
const util = require('util');
/**
* This exception should be used when some phat of code is not implemented.
* @param {String} message Error message that will be used inside error.
* @inheritDoc Error
*/
function NotImplementedException(message) {
this.message = message;
Error.captureStackTrace(this, NotImplementedException);
}
util.inherits(NotImplementedException, Error);
NotImplementedException.prototype.name = 'NotImplementedException';
module.exports = {
NotImplementedException,
};
在我的项目的其他文件中,我必须在文件顶部有这行需求。
const Exceptions = require('./exceptions.js');
要使用这个错误,你只需要这个。
const err = Exceptions.NotImplementedException(`Request token ${requestToken}: The "${operation}" from "${partner}" does not exist.`);
完整方法实现的示例
const notImplemented = (requestToken, operation, partner) => {
logger.warn(`Request token ${requestToken}: To "${operation}" received from "${partner}"`);
return new Promise((resolve, reject) => {
const err = Exceptions.NotImplementedException(`Request token ${requestToken}: The "${operation}" from "${partner}" does not exist.`);
logger.error(err.message);
return reject(err);
});
};