Javascript toJSON自定义方法的Stackoverflow错误

时间:2012-10-09 08:25:50

标签: javascript json oop design-patterns

方案

阅读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. (具体)我解决了stackoverflow错误,将.toJSON名称更改为.display。不那么优雅。还有另一种解决方案吗?

2 个答案:

答案 0 :(得分:6)

认为这是因为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我可以使用fn

检测是否调用JSON.stringify
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;
        }
    })()
}

答案 1 :(得分:3)

问题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,一个简单的解决方案:

创建自定义stringify函数,忽略toJSON方法(您可以将其添加到已存在的全局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;
}