Closure values get lost in a function passed as a callback to another function defined by new Function()
method.
How can the function baz()
be fixed to access the closure values in the callback?
Note: The function foo()
cannot be modified.
const foo = () => {
const b = 2;
return (a) => {
return a + b; // unable to access `b` here
};
};
const bar = (a = 1, callback = foo()) => callback(a);
const baz = new Function(["a = 1", `callback = ${foo()}`], "return callback(a)");
console.log(bar(1)); // works fine and prints 3
console.log(baz(1)); // throws Uncaught ReferenceError: b is not defined
Don't use string interpolation with a function. What is happening can be seen here:
const foo = () => {
const b = 2;
return (a) => a+b;
};
console.log(new Function(["a = 1", `callback = ${foo()}`], "return callback(a)"))
The function (a) => a+b
gets converted to a string, which is then put in the source code of the new function. Yes, this looses the closure - it looses the entire function object.
Instead, just write
const foo = () => {
const b = 2;
return (a) => a+b;
};
const bar = function(a = 1, callback = foo()) { return callback(a); };
const baz = new Function(["a = 1", "callback = foo()"], "return callback(a)");
console.log(bar(1));
console.log(baz(1));
console.log(bar);
console.log(baz);
As you can see from the logged functions, their code is now equivalent - and since everything in the demo snippet is global, they also work the same.
It's a bit different when foo
(or anything else you want to access from the function code string) is defined in a local scope. In that case, you'll need to use a trick like this to explicitly make the value available to the dynamically generated code.