【问题标题】:How to store a javascript function in JSON如何在 JSON 中存储 javascript 函数
【发布时间】:2016-07-30 18:06:30
【问题描述】:

我有一个 JS 对象我想保存在本地存储中以备将来使用,但我无法将其解析为字符串。

代码:

JSON.stringify({
    a: 5,
    b: function (param) {
        return param;
    }
})

结果:

"{"a":5}"

如果不使用 JSON,我如何保存它以供将来使用?

(并创建我自己的 Lexer-Parser 来中断字符串函数,我认为这不是一个选项)

【问题讨论】:

  • 一般情况下,你不能。一个原因是函数通常需要封闭作用域,它可以在其中找到它使用的一些变量。在非常特殊的情况下,您可以使用 Function 构造函数。
  • "(并创建我自己的 Lexer-Parser 来中断字符串函数,我认为这不是一个选项)" 嗯,它一个选项。可能不是一个好的选择。
  • 这几乎是一个 X/Y 问题。为什么需要将函数存储在本地存储中?
  • @DenysSéguret 如果是(如上)不使用任何超出范围的参数,是否可以存储?
  • @T.J.Crowder 我正在使用一个表格库,其中对于每一列我都有一个“cellRenderer”函数,我在其中操作列的单元格。您可以更改顺序和删除列,我想“保存”表的状态以创建新的“视图”以供将来使用

标签: javascript json


【解决方案1】:

您不能在 JSON 中存储函数。

JSON 中的值只能包含 stringnumberobjectarraytruefalsenull:

查看on JSON site

【讨论】:

  • 有没有办法将函数转换为字符串并向后转换?
  • "没有办法在 JSON 中存储函数。" ——那不是真的。这只是表明没有本机函数数据类型。函数可以表示为字符串。
  • @denysdovhan — 哦,不是,但“糟糕的想法”与“不可能”相差甚远
  • @Amit 是的,您可以使用(function () { ... }).toString() 执行此操作,但这不是一个好方法。也许你需要多考虑一下你的情况才能在不使用函数的情况下解决这个问题。
  • 这个答案在技术上是正确的 IMO。您不是在存储一个函数,而是在存储一个可以评估为函数的字符串。
【解决方案2】:

通常这样的问题表明存在 X/Y 问题:你需要做 X,你认为 Y 会帮助你做到这一点,所以你尝试做 Y,做不到,然后问如何做 Y。它会经常问如何做 X 更有用。

但回答问题:您可以使用 replacer 和 reviver 函数将函数转换为字符串(在 stringify 期间)并返回到函数(在 parse 期间)以存储函数的字符串版本,但这样做有各种各样的问题,尤其是定义函数的范围可能对函数很重要。 (与您在问题中显示的功能无关,但我认为这并不具有代表性。)将本地存储中的字符串转换为您可以运行的代码意味着您相信本地存储内容没有t 被恶意破坏。当然,除非页面已经容易受到 XSS 攻击,否则这不太可能,但这是一个需要牢记的问题。

这是一个例子,但我不推荐它,除非其他选项已经用尽,尤其是因为它使用eval,它(就像它的近亲new Function)可能是恶意代码的载体:

// The object
var obj = {
    a: 5,
    b: function (param) {
        return param;
    }
};

// Convert to JSON using a replacer function to output
// the string version of a function with /Function(
// in front and )/ at the end.
var json = JSON.stringify(obj, function(key, value) {
  if (typeof value === "function") {
    return "/Function(" + value.toString() + ")/";
  }
  return value;
});

// Convert to an object using a reviver function that
// recognizes the /Function(...)/ value and converts it
// into a function via -shudder- `eval`.
var obj2 = JSON.parse(json, function(key, value) {
  if (typeof value === "string" &&
      value.startsWith("/Function(") &&
      value.endsWith(")/")) {
    value = value.substring(10, value.length - 2);
    return (0, eval)("(" + value + ")");
  }
  return value;
});
document.body.innerHTML = obj2.b(42);

(0, eval)("(" + value + ")"); 构造确保eval 在全局范围内运行,而不是在 reviver 函数的范围内运行。通常eval 具有使用您调用它的作用域的神奇能力,但这仅在您直接调用它时才有效。 如图所示的间接eval(或只是var e = eval; e("(" + value + ")");)没有那种神奇的能力,它在全局范围内运行。

【讨论】:

  • 不太可能?嗯,如果网站在任何地方都容易受到 XSS 攻击,可能比我们想象的更有可能。我当然希望提问者重新考虑他的方法。
  • @user161778:如果网站已经容易受到 XSS 攻击,我认为这不会让情况变得更糟。如果不是,由于本地存储与源相关联,我认为这不会打开一扇新门。但我同意:我不会这样做。
  • 非常感谢,这个解决方案有效!这个项目没有被广泛访问,它只适用于我的公司,我相信我的项目存在 XSS 漏洞,但据我所知,这并没有打开新的大门。如果我将函数存储在数据库中,当然,有人可以向每个人注入代码。
  • 这取决于网站,我见过显示随机用户内容的网站被利用。这实际上意味着一些用户可能会在该网站上持续被利用,而不是仅仅在他们访问该页面时(因为标准网页代码是评估任意 JS 的代码)。我想如果不知道 OP 的应用程序就很难知道。
  • 有点死神,不过不用eval,你可以把字符串传给Function(+value.substring()+)developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 函数构造函数确实有一个更有限的范围来防止一些eval 攻击
【解决方案3】:

一种简单的方法是

var dstr = JSON.stringify( { a: 5
                           , b: x => x
                           }
                         , (k,v) => typeof v === "function" ? "" + v : v
                         );

【讨论】:

  • 太棒了!我不知道是否会因某些功能而失败,具体取决于它的内容,但我会改进它,这对我来说似乎是最干净的方式
  • 另外,反过来是JSON.parse(json, (k,v) => typeof v === "string"? (v.startsWith('function')? eval("("+v+")") : v): v)
【解决方案4】:

我已将函数名称与参数值一起存储在一个数组中,数组中的第一项是函数名称,前面带有 $,以便将它们与普通数组分开。

{
    "object": {
        "your-function": ["$functionName", "param-1", "param-2"],
        "color": ["$getColor", "brand", "brand-2"],
        "normal-array": ["normal", "array"]
        ...
    }
}

在上面的示例中,我使用 Sass 和 JS 函数从全局地图/对象中检索颜色值。以这种方式解析函数自然需要自定义代码,但在 JSON 中“存储”函数方面,我喜欢这种方式。

【讨论】:

    【解决方案5】:

    我推荐这种方法:

    将参数和正文存储在您的 json 中:

    {"function":{"arguments":"a,b,c","body":"return a*b+c;"}}
    

    现在解析json并实例化函数:

    var f = new Function(function.arguments, function.body);
    

    我认为是节省

    【讨论】:

    • 这帮助我解决了一个几乎不相关的问题。谢谢!
    • 解构也很适合这个!这也为我省去了很多麻烦!
    • 它是保存json的方式,但出于安全原因不保存。 new Function 总是在全局范围内创建一个函数。这至少是一个安全风险。
    【解决方案6】:

    我根据第一个答案创建了JSON.parseIt()JSON.stringifyIt() 函数,而没有使用eval

    JSON.stringifyIt = (obj)=>{
        return(
            JSON.stringify(obj, function(key, value) {
                if (typeof value === "function") {
                    return "/Function(" + value.toString() + ")/";
                }
                if(typeof value === "string"){
                    return "/String(" + value.toString() + ")/"
                }
                return value;
            })
        )
    }
    JSON.parseIt=(json)=>{
        return(
            JSON.parse(json, function(key, value) {
                if (typeof value === "string" &&
                value.startsWith("/Function(") &&
                value.endsWith(")/")) {
                    value = value.substring(10, value.length - 2);
                    var string = value.slice(value.indexOf("(") + 1, value.indexOf(")"));
                    if(/\S+/g.test(string)){
                        return (new Function(string,value.slice(value.indexOf("{") + 1, value.lastIndexOf("}"))))
    
                    }else{
                        return (new Function(value.slice(value.indexOf("{") + 1, value.lastIndexOf("}"))));
                    }
                    
                }
                if (typeof value === "string" &&
                value.startsWith("/String(") &&
                value.endsWith(")/")){
                    value = value.substring(8, value.length - 2);
                }
                return value;
            })
        )
    }
    
    // DEMO
    
    var obj = {
        string:"a string",
        number:10,
        func:()=>{
            console.log("this is a string from a parsed json function");
        },
        secFunc:(none,ntwo)=>{console.log(none + ntwo)} ,
        confuse:"/Function(hello)/"
    }
    const stringifiedObj = JSON.stringifyIt(obj);
    console.log("the stringified object is: ",stringifiedObj);
    
    const parsedObj = JSON.parseIt(stringifiedObj);
    
    // console.log("the parsed object is:  ",parsedObj);
    console.log(parsedObj.string);
    console.log(parsedObj.number);
    console.log(parsedObj.confuse);
    parsedObj.func();
    parsedObj.secFunc(5,6);

    我解决的问题是

    • 删除了评估。
    • 在字符串化和解析中出现问题,如果我给出一个类似的字符串 "/Function(hello)/" 解析时会是一个函数
    • 实现了两个功能
    • 添加参数插入

    【讨论】:

      猜你喜欢
      • 2014-07-04
      • 1970-01-01
      • 1970-01-01
      • 2021-04-02
      • 1970-01-01
      • 2012-07-10
      • 1970-01-01
      • 2016-01-02
      • 2017-12-13
      相关资源
      最近更新 更多