3 回答

TA贡献1942条经验 获得超3个赞
做到这一点的唯一好方法是从包含所有函数foo最终引用的父作用域开始。例如,对于您的fooand bar,如果您想传递foo到另一个bar可调用的上下文中,请传递一个同时声明fooandbar并返回的函数foo。例如:
const makeFoo = () => {
let bar = () => { alert(1) }
let foo = () => { bar() }
return foo;
};
const makeFooStr = makeFoo.toString();
// ...
const makeFooFunc = new Function(' return (' + makeFooStr + ').apply(null, arguments)');
const foo = makeFooFunc();
foo();
很好地实现这类事情确实需要像上面那样有预谋的设计(不幸的是)。在字符串化时,您不能真正包含所有祖先 LexicalEnvironments(变量名称到给定范围内的值的内部映射)。

TA贡献1909条经验 获得超7个赞
我想知道是否有办法递归地对函数进行字符串化?
我认为我们可以相当简单地证明这在一般情况下是不可能的。
让我们想想这两个函数
const greet = (greeting) => (name) => `${greeting} ${name}`
const sayHi = greet ('Hi')
sayHi ('Jane') //=> "Hi Jane"
虽然你foo和bar例子,我们可能想象的东西,审查的功能,在当前范围内做基于解析功能,并知道什么实际使用的局部变量的扩展功能字符串化使用提供一切的身体。(我猜这也是不可能的,原因与赖斯定理有关,但我们当然可以想象。)
但在这里,请注意
sayHi.toString() //=> "(name) => `${greeting} ${name}`"
因此sayHi取决于一个未存储在我们当前作用域中的自由变量,即greeting. 我们只是没有在任何地方存储用于创建该函数的“Hi”,除了在 的闭包范围内sayHi,它没有在任何地方公开。
所以即使是这个简单的函数也不能可靠地序列化;对更复杂的事情似乎没有希望。

TA贡献1811条经验 获得超4个赞
我最终滚动的灵感来自@CertainPerformance 的回答。
诀窍是构建一个定义所有子被调用函数的函数。然后你就拥有了字符串化父函数所需的一切。
注意:为了允许从其他文件导入被调用函数,我决定用被调用定义以编程方式构建一个字符串,而不是最初在同一范围内定义它们。
代码:
// original function definitions (could be in another file)
let bar = () => { alert(1) }
let foo = () => { bar() }
const allCallees = [ bar, foo ]
// build string of callee definitions
const calleeDefinitions = allCallees.reduce(
(definitionsString, callee) => {
return `${definitionsString} \n const ${callee.name} = ${callee.toString()};`;
},
"",
);
// wrap the definitions in a function that calls foo
const fooString = `() => { ${calleeDefinitions} \n return foo(); \n }`;
console.log(fooString);
/**
* fooString looks like this:
* `() => {
* const bar = () => { alert(1) };
* const foo = () => { bar() };
* return foo();
* }`
**/
// in new context & scope
const evaluatedFoo = new Function(' return (' + fooString + ').apply(null, arguments)');
// works as expected
evaluatedFoo();
添加回答
举报