admin管理员组文章数量:1295661
In our NodeJS application, we define custom error classes by extending the default Error object:
"use strict";
const util = require("util");
function CustomError(message) {
Error.call(this);
Error.captureStackTrace(this, CustomError);
this.message = message;
}
util.inherits(CustomError, Error);
This allows us to throw CustomError("Something");
with the stack trace showing up correctly, and both instanceof Error
and instanceof CustomError
working correctly.
However, for returning errors in our API (over HTTP), we want to convert the error to JSON. Calling JSON.stringify()
on an Error results in "{}"
, which is obviously not really descriptive for the consumer.
To fix this, I thought of overriding CustomError.prototype.toJSON()
, to return an object literal with the error name and message. JSON.stringify()
would then just stringify this object and all would work great:
// after util.inherits call
CustomError.prototype.toJSON = () => ({
name : "CustomError",
message : this.message
});
However, I quickly saw that this throws a TypeError: Cannot assign to read only property 'toJSON' of Error
. Which might make sense as I'm trying to write to the prototype. So I changed the constructor instead:
function CustomError(message) {
Error.call(this);
Error.captureStackTrace(this, CustomError);
this.message = message;
this.toJSON = () => ({
name : "CustomError",
message : this.message
});
}
This way (I expected), the CustomError.toJSON function would be used and the CustomError.prototype.toJSON (from Error) would be ignored.
Unfortunately, this just throws the error upon object construction: Cannot assign to read only property 'toJSON' of CustomError
.
Next I tried removing "use strict";
from the file, which sort of solved the problem in that no error was being thrown anymore, although the toJSON()
function was not used by JSON.stringify()
at all.
At this point I'm just desperate and just try random things. Eventually I end up with using Object.defineProperty()
instead of directly assigning to 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
})
});
This works perfectly. In strict mode, no errors are being called, and JSON.stringify()
returns {"name:" CustomError", "message": "Something"}
like I want it to.
So although it works as I want it to now, I still want to know:
- Why does this work exactly? I expect it to be the equivalent to
this.toJSON = ...
but apparently it is not. - Should it work like this? I.e. is it safe to depend on this behaviour?
- If not, how should I override the toJSON method correctly? (if possible at all)
In our NodeJS application, we define custom error classes by extending the default Error object:
"use strict";
const util = require("util");
function CustomError(message) {
Error.call(this);
Error.captureStackTrace(this, CustomError);
this.message = message;
}
util.inherits(CustomError, Error);
This allows us to throw CustomError("Something");
with the stack trace showing up correctly, and both instanceof Error
and instanceof CustomError
working correctly.
However, for returning errors in our API (over HTTP), we want to convert the error to JSON. Calling JSON.stringify()
on an Error results in "{}"
, which is obviously not really descriptive for the consumer.
To fix this, I thought of overriding CustomError.prototype.toJSON()
, to return an object literal with the error name and message. JSON.stringify()
would then just stringify this object and all would work great:
// after util.inherits call
CustomError.prototype.toJSON = () => ({
name : "CustomError",
message : this.message
});
However, I quickly saw that this throws a TypeError: Cannot assign to read only property 'toJSON' of Error
. Which might make sense as I'm trying to write to the prototype. So I changed the constructor instead:
function CustomError(message) {
Error.call(this);
Error.captureStackTrace(this, CustomError);
this.message = message;
this.toJSON = () => ({
name : "CustomError",
message : this.message
});
}
This way (I expected), the CustomError.toJSON function would be used and the CustomError.prototype.toJSON (from Error) would be ignored.
Unfortunately, this just throws the error upon object construction: Cannot assign to read only property 'toJSON' of CustomError
.
Next I tried removing "use strict";
from the file, which sort of solved the problem in that no error was being thrown anymore, although the toJSON()
function was not used by JSON.stringify()
at all.
At this point I'm just desperate and just try random things. Eventually I end up with using Object.defineProperty()
instead of directly assigning to 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
})
});
This works perfectly. In strict mode, no errors are being called, and JSON.stringify()
returns {"name:" CustomError", "message": "Something"}
like I want it to.
So although it works as I want it to now, I still want to know:
- Why does this work exactly? I expect it to be the equivalent to
this.toJSON = ...
but apparently it is not. - Should it work like this? I.e. is it safe to depend on this behaviour?
- If not, how should I override the toJSON method correctly? (if possible at all)
- 1 Take a look at my answer here and see if that is what you're asking about. Thanks! – Qantas 94 Heavy Commented Sep 28, 2015 at 11:40
-
@Qantas94Heavy That's pretty weird behaviour indeed, but at least explains why I can't assign to the property normally. What I still don't get from the linked article in your ment: does
Object.defineProperty
work (i.e. override read-only prototype properties anyway) by design or is this unexpected behaviour upon I should not rely? – tkers Commented Sep 28, 2015 at 12:06 -
I'm getting a
TypeError: Cannot redefine property: xxxx
– Angelos Pikoulas Commented Oct 6, 2023 at 17:47
3 Answers
Reset to default 4Why does this work exactly?
Object.defineProperty
just defines a property (or alters its attributes, if it already exists and is configurable). Unlike an assignment this.toJSON = …
it does not care about any inheritance and does not check whether there is an inherited property that might be a setter or non-writable.
Should it work like this? I.e. is it safe to depend on this behaviour?
Yes, and yes. Probably you can even use it on the prototype.
For your actual use case, given a recent node.js version, use an ES6 class that extends Error
for the best results.
Since I noticed you using the arrow function I'm going to assume you have access to ES6, meaning you have access to classes too.
You can just simply extend the Error
class. For example:
class CustomError extends Error {
toJSON() {
return {
name: 'CustomError',
message: this.message
};
}
}
If you truly need to use ponents or other React features inside your <template>
element you can fix the React to render in content
by monkey-patching a few of DOM methods of HTMLTemplateElement.
HTMLTemplateElement.prototype.appendChild = function (child) {
return this.content.appendChild(child);
}
HTMLTemplateElement.prototype.removeChild = function (child) {
return this.content.removeChild(child);
}
Object.defineProperty(HTMLTemplateElement.prototype, "firstChild", {
get() {
return this.content.firstChild;
}
})
I was able to render anything inside a template element after this. The appendChild
and removeChild
is mandatory because it will be used for render but if you're not using hydration you may not need the firstChild
patch.
本文标签: javascriptObjectdefineProperty to override readonly propertyStack Overflow
版权声明:本文标题:javascript - Object.defineProperty to override read-only property - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741622648a2388900.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论