Javascript 是否有可能使用JSON.stringify保留函数?

Javascript 是否有可能使用JSON.stringify保留函数?,javascript,json,object,Javascript,Json,Object,以这个物体为例: x = { "key1": "xxx", "key2": function(){return this.key1} } 如果我这样做: y = JSON.parse( JSON.stringify(x) ); 然后y将返回{“key1”:“xxx”}。通过stringify可以传递函数吗?使用“ye goode olde eval()”可以创建带有附加函数的对象,但打包又有什么用?从技术上讲,这不是JSON,我也很难想象您为什么要这样做,但请尝试以下方法: x.key2

以这个物体为例:

x = {
 "key1": "xxx",
 "key2": function(){return this.key1}
}
如果我这样做:

y = JSON.parse( JSON.stringify(x) );

然后y将返回
{“key1”:“xxx”}
。通过stringify可以传递函数吗?使用“ye goode olde eval()”可以创建带有附加函数的对象,但打包又有什么用?

从技术上讲,这不是JSON,我也很难想象您为什么要这样做,但请尝试以下方法:

x.key2 = x.key2.toString();
JSON.stringify(x)  //"{"key1":"xxx","key2":"function (){return this.key1}"}"

当然,第一行可以通过在对象上递归迭代来实现自动化。反向操作更难-函数只是一个字符串,
eval
可以工作,但您必须猜测给定的键是否包含字符串化的函数代码。

据我所知,任何语言中都没有保存函数的序列化库。序列化是用来保存数据的。编译是用来保存函数的。

不能打包函数,因为它们关闭的数据对任何序列化程序都不可见。 即使是Mozilla的
uneval
也无法正确打包闭包

你最好的选择是使用复活剂和替代品

传递给JSON.parse的reviver函数应用于原始解析对象中从最深的键到最高级别的所有键:值对。在我们的例子中,这意味着名称和发现的属性将通过恢复器传递,然后包含这些键的对象将被传递

这是一个类似的帖子

通过这篇文章发现的一个片段可能对任何一个无意中发现这个答案的人都有用。它通过使用JSON.stringify中的参数和JSON.parse中的参数来工作

更具体地说,当一个值恰好是function类型时,通过
替换程序对其调用
.toString()
。当需要解析时,当函数以字符串形式存在时,
eval()
通过
恢复程序执行

var JSONfn;
if (!JSONfn) {
    JSONfn = {};
}

(function () {
  JSONfn.stringify = function(obj) {
    return JSON.stringify(obj,function(key, value){
            return (typeof value === 'function' ) ? value.toString() : value;
        });
  }

  JSONfn.parse = function(str) {
    return JSON.parse(str,function(key, value){
        if(typeof value != 'string') return value;
        return ( value.substring(0,8) == 'function') ? eval('('+value+')') : value;
    });
  }
}());

代码片段摘自Vadim Kiryukhin,或参见位于

的文档。似乎登陆这里的人正在处理的结构如果不是因为它们包含函数的事实,将是有效的JSON。那么我们如何处理这些结构的串接呢

我在编写脚本修改RequireJS配置时遇到了这个问题。我就是这样做的。首先,前面有一段代码确保内部使用占位符(
“>>>F>>F顽皮但有效的方法是:

Function.prototype.toJSON = function() { return this.toString(); }

虽然您真正的问题(除了修改
函数的原型之外)是在不使用
eval的情况下反序列化
eval

,但是完全可以从字符串创建函数,而不使用
eval()

var obj = {a:function(a,b){
    return a+b;
}};

var serialized = JSON.stringify(obj, function(k,v){
    //special treatment for function types
    if(typeof v === "function")
        return v.toString();//we save the function as string
    return v;
});
/*output:
"{"a":"function (a,b){\n        return a+b;\n    }"}"
*/
现在,使用这个函数将字符串转换为函数是一种神奇的方法

var compileFunction = function(str){
    //find parameters
    var pstart = str.indexOf('('), pend = str.indexOf(')');
    var params = str.substring(pstart+1, pend);
    params = params.trim();

    //find function body
    var bstart = str.indexOf('{'), bend = str.lastIndexOf('}');
    var str = str.substring(bstart+1, bend);

    return Function(params, str);
}
现在将JSON.parse与reviver一起使用

var revivedObj = JSON.parse(serialized, function(k,v){
    // there is probably a better way to determ if a value is a function string
    if(typeof v === "string" && v.indexOf("function") !== -1)
        return compileFunction(v);
    return v;
});

//output:

 revivedObj.a

 function anonymous(a,b
 /**/) {

    return a+b;

 }

 revivedObj.a(1,2)
3
这就是我所做的


我最近也有类似的需求,很明显,输出看起来像JSON,但实际上只是javascript

JSON.stringify
在大多数情况下运行良好,但在函数方面“失败”

我通过一些技巧实现了这一点:

  • 利用
  • 使用
    func.toString()
    获取函数的JS代码
  • 记住哪些函数已经字符串化,并在结果中直接替换它们
  • 下面是它的样子:

    //我们的源数据
    常量源={
    “aaa”:123,
    “bbb”:功能(c){
    //做点什么
    返回c+1;
    }
    };
    //保留序列化函数的列表
    常量函数=[];
    //json replacer-返回函数的占位符
    const jsonReplacer=函数(键,val){
    if(typeof val==‘function’){
    functions.push(val.toString());
    返回“{func"+(functions.length-1)+“}”;
    }
    返回val;
    };
    //regex replacer-用函数替换占位符
    const funcReplacer=函数(匹配,id){
    返回函数[id];
    };
    const result=JSON
    .stringify(source,jsonReplacer)//使用占位符生成json
    .replace(/“\{func\(\d+)\}”/g,funcReplacer);//用函数替换占位符
    //显示结果
    document.body.innerText=结果;

    body{white-space:pre-wrap;font-family:monospace;}
    为什么不在函数周围加引号,然后
    eval()
    它呢?JSON不允许函数。如果它允许,它也不会比
    eval
    更好或更安全。这就是
    toJSON
    的目的吗?
    JSON
    !=
    javascript对象
    一篇帖子演示:[警告]:
    eval(v)
    很容易被反序列化的恶意函数利用,我会更乐意将这些函数作为字符串发出,因此任何实现都是在
    parseWithFunctions
    之外显式完成的。
    var revivedObj = JSON.parse(serialized, function(k,v){
        // there is probably a better way to determ if a value is a function string
        if(typeof v === "string" && v.indexOf("function") !== -1)
            return compileFunction(v);
        return v;
    });
    
    //output:
    
     revivedObj.a
    
     function anonymous(a,b
     /**/) {
    
        return a+b;
    
     }
    
     revivedObj.a(1,2)
    3
    
    function stringifyWithFunctions(object) {
      return JSON.stringify(object, (key, val) => {
        if (typeof val === 'function') {
          return `(${val})`; // make it a string, surround it by parenthesis to ensure we can revive it as an anonymous function
        }
        return val;
      });
    };
    
    function parseWithFunctions(obj) {
      return JSON.parse(obj, (k, v) => {
        if (typeof v === 'string' && v.indexOf('function') >= 0) {
          return eval(v);
        }
        return v;
      });
    };