Because javascript functions are not serializable, in order to pass them into new contexts sometimes (albeit rarely) it can be useful to stringify them then re-evaluate them later like:
const foo = () => { // do something }
const fooText = foo.toString()
// later... in new context & scope
const fooFunc = new Function(' return (' + fooText + ').apply(null, arguments)')
fooFunc() // works!
However, if foo
references another function bar
, the scope is not stringified, so if bar
is not defined in the new context, the evaluated foo function will throw an error when called.
That is, not only stringifying the parent function, but also stringifying the contents of the child functions called from the parent.
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) }"
Let me know if you have any ideas on how to accomplish something like a "recursiveToString"
The only good way to do this is to start from a parent scope that encloses all functions foo
eventually references. For example, with your foo
and bar
, if you want to pass foo
into another context such that bar
is callable as well, pass a function that declares both foo
and bar
, and returns foo
. For example:
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();
Implementing this sort of thing well does require premeditated design like above (unfortunately). You can't really include all ancestor LexicalEnvironments (the internal map of variable names to values in a given scope) when stringifying.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With