猿问

如何“递归地”将调用其他作用域函数的 javascript 函数字符串化?

因为 javascript 函数是不可序列化的,为了有时(尽管很少)将它们传递到新的上下文中,将它们字符串化然后稍后重新评估它们会很有用,例如:


const foo = () => { // do something }

const fooText = foo.toString()


// later... in new context & scope

const fooFunc = new Function(' return (' + fooText + ').apply(null, arguments)')

fooFunc() // works!

但是,如果foo引用另一个 function bar,则范围未字符串化,因此如果bar未在新上下文中定义,则评估的 foo 函数将在调用时抛出错误。


我想知道是否有办法递归地对函数进行字符串化?

也就是说,不仅将父函数字符串化,还要将父函数调用的子函数的内容字符串化。


例如:

let bar = () => { alert(1) }

let foo = () => { bar() }


// what toString does

let fooString = foo.toString()

console.log(fooString) // "() => { bar() }"


// what we want

let recursiveFooString = foo.recursiveToString()

console.log(recursiveFooString) // "() => { alert(1) }"


如果您对如何完成诸如“recursiveToString”之类的事情有任何想法,请告诉我


Helenr
浏览 171回答 3
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(变量名称到给定范围内的值的内部映射)。

jeck猫

我想知道是否有办法递归地对函数进行字符串化?我认为我们可以相当简单地证明这在一般情况下是不可能的。让我们想想这两个函数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,它没有在任何地方公开。所以即使是这个简单的函数也不能可靠地序列化;对更复杂的事情似乎没有希望。

波斯汪

我最终滚动的灵感来自@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();
随时随地看视频慕课网APP

相关分类

JavaScript
我要回答