Object.defineProperty重写只读属性

tke*_*ers 5 javascript prototype properties node.js ecmascript-6

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

"use strict";
const util = require("util");

function CustomError(message) {
    Error.call(this);
    Error.captureStackTrace(this, CustomError);
    this.message = message;
}

util.inherits(CustomError, Error);
Run Code Online (Sandbox Code Playgroud)

这使我们能够throw CustomError("Something");与堆栈跟踪正确显示出来,并且都instanceof Errorinstanceof CustomError正常工作。

但是,为了通过我们的API返回错误(通过HTTP),我们希望将错误转换为JSON。调用JSON.stringify()错误会导致"{}",这显然对消费者而言并不是真正的描述。

为了解决这个问题,我想到了重写CustomError.prototype.toJSON(),以返回带有错误名称和消息的对象文字。JSON.stringify()然后只需将这个对象字符串化,一切都会很好:

// after util.inherits call

CustomError.prototype.toJSON = () => ({
    name    : "CustomError",
    message : this.message
});
Run Code Online (Sandbox Code Playgroud)

但是,我很快就看到这引发了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
    });
}
Run Code Online (Sandbox Code Playgroud)

这样(我期望),将使用CustomError.toJSON函数,并且将忽略CustomError.prototype.toJSON(来自Error)。

不幸的是,这只会在对象构造时引发错误:Cannot assign to read only property 'toJSON' of CustomError

接下来,我尝试"use strict";从文件中删除,这种方法解决了该问题,尽管toJSON()不再使用该函数,但不再抛出任何错误。JSON.stringify()

在这一点上,我只是拼命,只是尝试随机的东西。最终,我最终使用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
        })
    });
Run Code Online (Sandbox Code Playgroud)

这很完美。在严格模式下,不会调用任何错误,并且会JSON.stringify()返回{"name:" CustomError", "message": "Something"}像我希望的那样。

因此,尽管它现在可以按我的意愿运行,但我仍然想知道:

  1. 为什么这完全起作用?我希望它相当于this.toJSON = ...但显然不是。
  2. 应该这样吗?即依靠这种行为是否安全?
  3. 如果没有,应该如何正确重写toJSON方法?(如果可能的话)

Ber*_*rgi 3

\n

为什么这确实有效?

\n
\n\n

Object.defineProperty只是定义一个属性(或者更改其属性,如果它已经存在并且是可配置的)。与赋值不同,this.toJSON = \xe2\x80\xa6它不关心任何继承,也不检查是否存在可能是 setter 或不可写的继承属性。

\n\n
\n

应该像这样工作吗?即依赖这种行为安全吗?

\n
\n\n

是的,是的。也许您甚至可以在原型上使用它。

\n\n
\n\n

对于您的实际用例,给定最新的 Node.js 版本,请使用 ES6 类extends Error以获得最佳结果。

\n