This code works optimally and is easily comprehensible:
function evalInScope(js, contextAsScope) {
//# Return the results of the in-line anonymous function we .call with the passed context
return function() {
with(this) {
return eval(js);
};
}.call(contextAsScope);
}
evalInScope("a + b", {a: 1, b: 2}); // 3 obviously, but fails in strict mode!
However, the "smart" brains decided to remove the with
statement without a proper replacement.
Question: how to make it work again in ES6, which is automatically in strict mode?
Don't use eval
, create a new Function
instead. It won't inherit lexical strict mode - and even better, it won't inherit all your function-scoped and module-scoped variables:
"use strict";
function evalInScope(js, contextAsScope) {
return new Function(`with (this) { return (${js}); }`).call(contextAsScope);
}
console.log(evalInScope("a + b", { a: 1, b: 2 })); // 3
Also you don't get the weird "(last) statement result" return value that eval
uses, but can either confine the js
code to be an expression or include a return
statement in the code itself.
Alternatively, if you don't actually need to use a with
statement with all its intricacies but just want to make a dynamic set of constant variables available to the eval
ed code, just generate the code for those constants dynamically. This allows to eval
the code in strict mode even:
"use strict";
function evalInScope(js, contextAsScope) {
return new Function(
`"use strict";
const {${Object.keys(contextAsScope).join(', ')}} = this;
return (${js});`
).call(contextAsScope);
}
console.log(evalInScope("a + b", { a: 1, b: 2 })); // 3
Or if the code doesn't use the this
keyword itself, maybe also
"use strict";
function evalInScope(js, contextAsScope) {
return new Function(
'{' + Object.keys(contextAsScope).join(', ') + '}',
`return (${js});`
)(contextAsScope);
}
console.log(evalInScope("a + b", { a: 1, b: 2 })); // 3