使用 JSON 对 js 对象进行分级/解析时,返回的方法中的函数作用域会丢失

Function scope is lost in returned methods when strigifying/parsing js objects with JSON

本文关键字:方法 返回 函数 作用域 对象 js JSON 使用      更新时间:2023-09-26

我有一个有点疯狂的例子,但对于那些精通javascript函数作用域的人来说,这似乎是一个很好的练习:

(function (global) {
  // our module number one
  var body = function () {
    var someVar = 'some test text';
    return {
        method: function () {
            return someVar; // this will undefined when call this method in second module
        }
    };
  };
  var f = new Function([' return (', body, ')();'].join(''));
  global.r = f();
})(window);
(function (global) {
  // our module two
  var body = function () {
    // dep variable is injected on the fly with `new Function`
    dep.method(); // will throw `Reference Error: someVar is not defined`
  };
  // helper to propertly transform functions in JSON.stringify
  function transformFuncs (key, val) {
    if (typeof val === 'function') {
        return val.toString().replace(/('t|'n|'r)/gm, '').replace(/("|')/gm, '''"');
    }
    return val;
  }
  // injecting our first module inside
  var vars = ['var ', 'dep', ' = JSON.parse(''', JSON.stringify(global.r, transformFuncs), ''', function (key, value) { if (value && typeof value === "string" && value.substr(0,8) == "function") { var startBody = value.indexOf("{") + 1; var endBody = value.lastIndexOf("}"); var startArgs = value.indexOf("(") + 1; var endArgs = value.indexOf(")"); return new Function(value.substring(startArgs, endArgs), value.substring(startBody, endBody)); } return value; });'].join('');
  // as variable
  var f2 = new Function([vars, ' return (', body, ')();'].join(''));
  global.r2 = f2();
})(window);

如果要在某处运行此代码,则会看到引发ReferenceError: someVar is not defined异常。

所以基本上这里发生了什么 - 我们创建一些模块,然后尝试将其作为变量注入另一个模块中。JSON.parse中用于正确获取字符串化函数的函数如下所示(如果您好奇的话(:

function (key, value) { 
  if (value && typeof value === "string" && value.substr(0,8) == "function") { 
    var startBody = value.indexOf("{") + 1; 
    var endBody = value.lastIndexOf("}"); 
    var startArgs = value.indexOf("(") + 1; 
    var endArgs = value.indexOf(")"); 
    return new Function(value.substring(startArgs, endArgs), value.substring(startBody, endBody)); 
  } 
  return value; 
}

那么..问题是是否可以解决这种范围行为?据我了解global.r在一个作用域中分配了f结果,但具有method函数的结果对象不会保存变量实例,因为在 JSON 解析时为该函数创建了另一个范围。

有什么想法吗?

附言请不要问我为什么需要这个:)想想可能的解决方案。主要思想是以某种方式将第一模块(查看顶部body var(作为变量注入到第二个模块(第二个函数的body变量(中,保存返回的方法的原始范围

谢谢!

是否可以解决此类范围行为?

不。作用域无法从外部访问,并且在函数序列化时必然会丢失。您只能串化不引用任何自由变量的"纯"函数,例如body

这也是JSON格式不包含函数的原因。

主要思想是以某种方式将第一个模块作为变量注入到第二个模块中,保存返回的方法的原始范围。

注入引用,而不是字符串。依赖注入不是魔法,JavaScript 的各种模块加载器都可以做到。