Javascript Object.defineProperty以重写只读属性

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)

在我们的NodeJS应用程序中,我们通过扩展默认错误对象来定义自定义错误类:

"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”

接下来,我尝试删除
“使用严格”解决了问题,不再抛出错误,尽管
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上复制。