JavaScript: 将对象字符串化(包括函数类型成员)

25

我正在寻找一种解决方案,可以在各个浏览器中将Javascript对象(包括其中的函数成员)序列化(和反序列化)为字符串。 一个典型的对象看起来像这样:

{
   color: 'red',
   doSomething: function (arg) {
        alert('Do someting called with ' + arg);
   }
}

doSomething()函数只包含局部变量(不需要将调用上下文序列化!)。

JSON.stringify()会忽略'doSomething'成员因为它是一个函数。我知道toSource()方法可以实现我想要的效果,但它只适用于FF浏览器。


1
我只留下评论,因为我没有提供完整的解决方案。一个可能有用的方法是,您可以使用toString()调用将函数转换为字符串。http://jsfiddle.net/HFMaz/ 不确定跨浏览器的支持情况。 - user113716
1
这个问题经常被问到,但我找不到相关的东西:\ 为什么你需要序列化函数呢?也许有更好的方法来安排你的代码? - lincolnk
这是用于所见即所得项目的内容。每个带有方法的Javascript对象定义了页面组件的行为。页面内容(包括JS行为)必须在服务器端保存,因此需要进行序列化处理。 - Yannick Deltroo
代码是如何创建的?看起来你应该在这个过程的早期阶段就能够访问所有这些的字符串表示。 - lincolnk
我希望这不会被广大公众使用,否则你必须确保我无法对其进行跨站脚本攻击。 - Ivo Wetzel
6个回答

14

您可以使用具有像下面这样的replacerJSON.stringify

JSON.stringify({
   color: 'red',
   doSomething: function (arg) {
        alert('Do someting called with ' + arg);
   }
}, function(key, val) {
        return (typeof val === 'function') ? '' + val : val;
});

4
最终我使用了JSON.stringify(myObject, function(key, val) { return (typeof val === 'function') ? '[function]' : val; }, 4);,输出结果很好。 - Julien Bérubé

8
一种快速而简单的方法是这样的:
Object.prototype.toJSON = function() {
  var sobj = {}, i;
  for (i in this) 
    if (this.hasOwnProperty(i))
      sobj[i] = typeof this[i] == 'function' ?
        this[i].toString() : this[i];

 return sobj;

};

显然,这将影响代码中每个对象的序列化,并可能使使用未经过滤的 "for in" 循环的天真代码出现问题。"正确" 的方法是编写一个递归函数,在任何给定对象的所有后代成员上添加 "toJSON" 函数,处理循环引用等问题。但是,假设单线程 Javascript(没有 Web Workers),这种方法应该能够正常工作,不会产生任何意外的副作用。
必须向 Array 的原型添加类似的函数,以覆盖 Object 的函数并返回一个数组而不是一个对象。另一种选择是附加一个单一的函数,并让它根据对象的本质有选择地返回数组或对象,但这可能会更慢。
function JSONstringifyWithFuncs(obj) {
  Object.prototype.toJSON = function() {
    var sobj = {}, i;
    for (i in this) 
      if (this.hasOwnProperty(i))
        sobj[i] = typeof this[i] == 'function' ?
          this[i].toString() : this[i];

    return sobj;
  };
  Array.prototype.toJSON = function() {
      var sarr = [], i;
      for (i = 0 ; i < this.length; i++) 
          sarr.push(typeof this[i] == 'function' ? this[i].toString() : this[i]);

      return sarr;
  };

  var str = JSON.stringify(obj);

  delete Object.prototype.toJSON;
  delete Array.prototype.toJSON;

  return str;
}

http://jsbin.com/yerumateno/2/edit


1
很好,除了数组不再被序列化为数组。 - DATEx2
3
解释:这是一道关于代码实现的问题,需要将字符串中存储的函数转换回真正的函数。翻译:解析函数应该长什么样?由于所有的函数都存储在字符串中,因此在解析时我们需要将它们转换回函数。 - Justin
嗨MooGoo,我在将其字符串化后遇到了困难,我们如何解析以获取原始函数? - GeniusGeek
嗨Justin,你有解决方案将其解析回原始函数吗? - GeniusGeek

1

如果没有对象本身的帮助,这是不可能的。例如,你要如何序列化这个表达式的结果?

(function () {
  var x; 
  return {
      get: function () { return x; },
      set: function (y) { return x = y; }
    };
})();

如果只取函数的文本部分,那么在反序列化时,x将引用全局变量,而不是闭包中的变量。此外,如果你的函数与浏览器状态密切相关(比如一个对 div 的引用),你需要考虑你希望它具有的含义。

当然,你可以为每个对象编写特定的方法,以确定对其他对象的引用应具备哪些语义。


1

我已经测试了我的对象,它有一个函数但是它没有工作。 - MERT DOĞAN

1

类似这样的东西...

(function(o) {
    var s = "";
    for (var x in o) {
        s += x + ": " + o[x] + "\n";
    }
    return s;
})(obj)

注意:这是一个表达式。它返回传递给它作为参数的对象的字符串表示形式(在我的例子中,我将其传递给名为obj的变量)。
您还可以重写Object原型的toString方法:
Object.prototype.toString = function() {
    // define what string you want to return when toString is called on objects
}

0

var myObj = {
  color: 'red',
  doSomething: function (arg) {
  alert('Do someting called with ' + arg);
  }
}

var placeholder = '____PLACEHOLDER____';
var fns = [];
var json = JSON.stringify(myObj, function(key, value) {
  if (typeof value === 'function') {
    fns.push(value);
    return placeholder;
  }
  return value;
}, 2);

json = json.replace(new RegExp('"' + placeholder + '"', 'g'), function(_) {
  return fns.shift();
});

console.log(json)

https://gist.github.com/cowboy/3749767


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接