将函数序列化和反序列化为JSON

dpq*_*dpq 4 javascript parsing json

假设您有以下内容:

function myfunc() {
  // JS code
}

var args = '{ "strfield": "hello world", "numfield": 10, "funcfield": myfunc }';
Run Code Online (Sandbox Code Playgroud)

问题:如何在将args变量提交给JSON解析器之前处理变量,以便将myfunc其替换为myfunc.toString()(即函数体)的结果?建议的解决方案应该适用于任意函数和这样的准JSON字符串.

小智 6

我们使用可选的第二个replacer参数来JSON.stringify预处理键/值,以字符串形式发出函数:

function stringify_with_fns(obj) {
    return JSON.stringify(obj, function(key, value) {
        return typeof value === "function" ? value.toString() : value;
    });
}
Run Code Online (Sandbox Code Playgroud)

然后在出路时将字符串化函数转换回实际函数,我们使用可选的第二个reviver参数JSON.parse,如

function parse_with_fns(json) {
    return JSON.parse(json, function(key, value) {
        if (looks_like_a_function_string(value)) {
            return make_function_from_string(value);
        } else {
            return value; 
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

looks_like_a_function_string只是一个正则表达式,第一个切入make_function_from_string可以使用eval:

function looks_like_a_function_string(value) {
    return /^function.*?\(.*?\)\s*\{.*\}$/.test(value);
}
function make_function_from_string(value) {
    return eval(value);
}
Run Code Online (Sandbox Code Playgroud)

为了避免eval,我们可以选择函数字符串来查找它的参数和主体,因此我们可以将它们传递给new Function:

function make_function_from_string(value) {
    var args = value
        .replace(/\/\/.*$|\/\*[\s\S]*?\*\//mg, '') //strip comments
        .match(/\(.*?\)/m)[0]                      //find argument list
        .replace(/^\(|\)$/, '')                    //remove parens
        .match(/[^\s(),]+/g) || [],                //find arguments
        body = value.match(/\{(.*)\}/)[1]          //extract body between curlies

    return Function.apply(0, args.concat(body);
}
Run Code Online (Sandbox Code Playgroud)

测试:

x = parse_with_fns(stringify_with_fns({a: function() {var x=1;}}))
x.a
> function anonymous() {var x=1;}
Run Code Online (Sandbox Code Playgroud)

但请注意,由于创建的函数new Function都在全局范围内,因此它们将失去其封闭范围和闭包.

唯一剩下的问题是这是否有用.我想它可能适用于小型实用功能.

将概念扩展为序列化/反序列化正则表达式或日期对象仍然是一个练习.