Javascript Object.defineProperty以重写只读属性
在我们的NodeJS应用程序中,我们通过扩展默认错误对象来定义自定义错误类:Javascript Object.defineProperty以重写只读属性,javascript,node.js,properties,ecmascript-6,prototype,Javascript,Node.js,Properties,Ecmascript 6,Prototype,在我们的NodeJS应用程序中,我们通过扩展默认错误对象来定义自定义错误类: "use strict"; const util = require("util"); function CustomError(message) { Error.call(this); Error.captureStackTrace(this, CustomError); this.message = message; } util.inherits(CustomError, Error)
"use strict";
const util = require("util");
function CustomError(message) {
Error.call(this);
Error.captureStackTrace(this, CustomError);
this.message = message;
}
util.inherits(CustomError, Error);
这允许我们抛出CustomError(“某物”)代码>堆栈跟踪正确显示,并且instanceof Error
和instanceof CustomError
都工作正常
但是,为了在API(通过HTTP)中返回错误,我们希望将错误转换为JSON。对错误调用JSON.stringify()
会导致“{}”
,这显然不是对使用者的真正描述
为了解决这个问题,我考虑重写CustomError.prototype.toJSON()
,以返回包含错误名称和消息的对象文本JSON.stringify()
只需将此对象字符串化,一切都会很好:
// after util.inherits call
CustomError.prototype.toJSON = () => ({
name : "CustomError",
message : this.message
});
然而,我很快发现这抛出了一个TypeError:cannotassign给只读属性'toJSON'的错误
。这可能是有道理的,因为我正试图写的原型。因此,我更改了构造函数:
function CustomError(message) {
Error.call(this);
Error.captureStackTrace(this, CustomError);
this.message = message;
this.toJSON = () => ({
name : "CustomError",
message : this.message
});
}
这样(我预期)将使用CustomError.toJSON函数,而CustomError.prototype.toJSON(from Error)将被忽略
不幸的是,这只会在对象构造时抛出错误:无法分配给CustomError的只读属性“toJSON”
接下来,我尝试删除“使用严格”该文件中的code>解决了问题,不再抛出错误,尽管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方法?(如果可能的话)
因为我注意到您使用了arrow函数,所以我假设您可以访问ES6,这意味着您也可以访问类
您只需扩展Error
类即可。例如:
class CustomError extends Error {
toJSON() {
return {
name: 'CustomError',
message: this.message
};
}
}
这到底是为什么
Object.defineProperty
只定义一个属性(或者改变其属性,如果它已经存在并且是可配置的)。与赋值this.toJSON=…
不同,它不关心任何继承,也不检查继承的属性是否可能是setter或不可写的
它应该这样工作吗?即,依赖这种行为安全吗
是的,是的。也许你甚至可以在原型上使用它
对于您的实际用例,给定一个最新的node.js版本,使用一个扩展错误的ES6类来获得最佳结果。看一看,看看这是否就是您要问的。谢谢!@Qantas94Heavy这确实是一个非常奇怪的行为,但至少解释了为什么我不能正常分配属性。我仍然没有得到m在您的评论中:是Object.defineProperty
通过设计起作用(即无论如何覆盖只读原型属性),还是我不应该依赖这种意外行为?好的一点,它看起来更干净了!不过,为什么这样做(以及使用Object.defineProperty
)工作并尝试设置this.toJSON
不会吗?实际上我不太确定,无法在节点4.x上复制。