How to replace `with` statement in strict mode

167 Views Asked by At

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?

2

There are 2 best solutions below

7
Bergi On BEST ANSWER

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 evaled 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

0
Cerbrus On

The problem is that you're trying to do something that strict mode specifically blocks.

So, basically, what you want to do isn't possible.

You're either going to have to choose not to use use strict mode, or you're going to have to work around the use of eval (I'd personally recommend never using eval...).