【问题标题】:Stackoverflow error on Javascript toJSON custom methodJavascript toJSON自定义方法上的Stackoverflow错误
【发布时间】:2012-09-29 12:51:35
【问题描述】:

场景

阅读this answer 后,我意识到我可以从 JSON 文字开始创建对象。

所以我猜我可以使用这个有用的 JSON 方法做相反的事情: JSON.stringify(myObject).

所以我做了如下:

function MyObject(id, value, desc)
{
  this.id = id;
  this.value = value;
  this.desc = desc;
  this.toJSON = function()
  {
    return JSON.stringify(this);
  }

}

但是当我运行这些东西 (demo) 时,会出现 Maximum call stack size exceeded 错误。

在谷歌上搜索了一下后,我找到了两个解释这种行为的参考资料:

如果我猜对了,.toJSON 会覆盖 .stringify。因此,如果第一个调用第二个,则会生成一个循环。

问题

  1. (一般)为什么选择这种设计? toJSON 是一种特殊关键字的保留?
  2. (具体)我解决了将.toJSON 名称更改为.display 的stackoverflow 错误。没有那么优雅。还有其他解决方案吗?

【问题讨论】:

  • +1 用于使用 stackoverflow 关键字。不,开玩笑,这是个好问题。
  • @ADC 记得接受您的未决问题。
  • 如果没有什么特别之处,为什么要使用自定义的toJSON 方法?

标签: javascript json oop design-patterns


【解决方案1】:

认为这是因为 toJSON 是半保留的:stringify 将检查对象并查看它是否有一个名为 toJSON 的方法,然后尝试调用它以将结果字符串化。


解决方法可以是:(不确定此代码的可靠性)

var obj = {
    value: 1,
    name: "John",
    toJSON: function() {
        var ret,
            fn = this.toJSON;

        delete this.toJSON;

        ret = JSON.stringify(this);

        this.toJSON = fn;

        return ret;
    }
}

用法:

obj.toJSON(); // "{\"value\":1,\"name\":\"John\"}"
obj.lastName = "Smith";
obj.toJSON(); // "{\"value\":1,\"name\":\"John\",\"lastName\":\"Smith\"}"

也许使用 clousure 更漂亮一点:(然后我想我可以说它是安全的)

var obj = {
    value: 1,
    name: "John",
    toJSON: (function() {
        function fn() {
            var ret;
            delete this.toJSON;

            ret = JSON.stringify(this);

            this.toJSON = fn;

            return ret;
        }
        return fn;
    })()
}

所以在阅读了@filmor 的评论后,我想到了另一种处理方式。不是很漂亮,但很管用。

使用Function.caller 我可以检测是否使用JSON.stringify 调用了fn

var obj = {
    value: 1,
    name: "John",
    toJSON: (function() {
        return function fn() {
            var ret;

            delete this.toJSON;

            ret = JSON.stringify(this);

            if ( fn.caller === JSON.stringify ) {
                ret = JSON.parse( ret );
            }

            this.toJSON = fn;

            return ret;
        }
    })()
}

【讨论】:

  • 发现这一点的时间与您大致相同(在课程期间 Firefox 崩溃之后;))。一条评论:如果你这样做,你会破坏正常的JSON.stringify(obj),因为它会将对象解释为字符串并转义并添加周围的 " 字符。
  • @filmor 我遇到了同样的问题。但我想我找到了使用fn.caller 的解决方案
  • 很有趣,但是很多东西只是为了使用toJSON 而不是display :)。请注意,Function.caller 页面中有 non standard 备注。无论如何,一个优雅的解决方案。
【解决方案2】:

问题 1,toJSON 是否保留?

我不确定它是否保留,但例如本机 Date 对象使用 toJSON 创建字符串化日期表示:

(new Date()).toJSON();           // -> "2012-10-20T01:58:21.427Z"
JSON.stringify({d: new Date()}); // -> {"d":"2012-10-20T01:58:21.427Z"}"

问题 2,一个简单的解决方案:

创建忽略 toJSON 方法的自定义 stringify 函数(您可以将其添加到已经存在的全局 JSON):

JSON.customStringify = function (obj) {

    var fn = obj.toJSON;
    obj.toJSON = undefined;
    var json = JSON.stringify(obj);
    obj.toJSON = fn;
    return json;
}

现在它在您的所有对象中都非常容易使用:

function MyObject(id, value, desc)
{
  this.id = id;
  this.value = value;
  this.desc = desc;
  this.toJSON = function()
  {
    return JSON.customStringify(this);
  }
}

为了更容易添加:

JSON.customStringifyMethod = function () {

    return JSON.customStringify(this);
}

现在您的对象可能如下所示:

function MyObject(id, value, desc)
{
  this.id = id;
  this.value = value;
  this.desc = desc;
  this.toJSON = JSON.customStringifyMethod;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-29
    • 2012-07-23
    • 1970-01-01
    相关资源
    最近更新 更多