"use strict";
const util = require("util");
function CustomError(message) {
Error.call(this);
Error.captureStackTrace(this, CustomError);
this.message = message;
}
util.inherits(CustomError, Error);
这使得我们可以使用
throw CustomError("Something");
,正确显示堆栈跟踪,并且instanceof Error
和instanceof CustomError
都能正常工作。但是,在API中返回错误(通过HTTP),我们需要将错误转换为JSON格式。在Error对象上调用
JSON.stringify()
会导致结果为"{}"
,这对于消费者来说显然不够描述性。为了解决这个问题,我想重写
CustomError.prototype.toJSON()
,返回一个包含错误名称和消息的对象字面量。然后,JSON.stringify()
将只对该对象进行字符串化处理,一切都将正常工作:// after util.inherits call
CustomError.prototype.toJSON = () => ({
name : "CustomError",
message : this.message
});
然而,我很快就发现这会抛出一个 TypeError: Cannot assign to read only property 'toJSON' of Error
。这也许是有道理的,因为我在尝试写入原型。所以我改变了构造函数:
function CustomError(message) {
Error.call(this);
Error.captureStackTrace(this, CustomError);
this.message = message;
this.toJSON = () => ({
name : "CustomError",
message : this.message
});
}
我原本期望的是这样,CustomError.toJSON函数会被使用,而Error中的CustomError.prototype.toJSON则会被忽略。
然而,这只会在对象构造时引发错误:Cannot assign to read only property 'toJSON' of CustomError
。
接下来,我尝试从文件中删除"use strict";
,有点解决了问题,因为不再抛出错误,但JSON.stringify()
根本没有使用toJSON()
函数。
此时,我已经万分绝望,开始尝试随机的方法。最终,我使用Object.defineProperty()
代替直接赋值给this.toJSON
:
function CustomError(message) {
Error.call(this);
Error.captureStackTrace(this, CustomError);
this.message = message;
Object.defineProperty(this, "toJSON", {
value: () => ({
name : "CustomError",
message : this.message
})
});
这个代码完美地运行了。在严格模式下,没有出现任何错误,并且JSON.stringify()
返回了{"name:" CustomError", "message": "Something"}
,就像我想要的一样。
所以虽然它现在按照我想要的方式工作,但我仍然想知道:
- 为什么它能正常工作?我期望它等同于
this.toJSON = ...
,但显然并不是这样。 - 它应该像这样工作吗?也就是说,依赖这种行为是否安全?
- 如果不是,我应该如何正确地覆盖toJSON方法?(如果可能的话)
Object.defineProperty
是否按设计工作(即是否覆盖只读原型属性),还是这是我不应该依赖的意外行为? - tkersTypeError: 无法重新定义属性: xxxx
的错误。 - undefined