I am trying to execute eval within a particular context. I have found the answer here useful. However I am getting the following behavior in Chrome Version 53.0.2785.143 m.
whilst I recommend the answer provided by @trincot and in particular the great link. I am posting here my solution to the problem I faced
function evalInContext(scr, context)
{
// execute script in private context
return (new Function( "with(this) { return " + scr + "}")).call(context);
}
The with(this)
expression allows the member variables of the context
object to be present in the execution scope of the expression scr
.
Credit to this answer to a similar question
This is an alternative method (written in ES6):
(new Function(...Object.keys(context), codeYouWantToExecute))(...Object.values(context));
Credits to Jonas Wilms for the answer.
Here is my explanation, since I did not initially understand how it worked:
let example = new Function('a', 'b', 'return a+ b')
is the same as creating the function and assigning it to example
:
function(a,b) {
return a + b
}
After that, you can call example
like this: example(2,6)
which returns 8.
But let's say you don't want to assign the function created by new Function
to a variable and you want to immediately call it instead.
Then you can just do this:
new Function('a', 'b', 'return a+ b')(2,6)
The code snippet at the top of this post is essentially doing the same thing. It uses the spread operator to pass in the keys of the context
object like this:
new Function(key1, key2, key3... lastkey, codeYouWantToExecute)
. It then calls the newly created function and passes the values that correspond to each key with the spread operator.
Lastly, here is the answer compiled to ES5:
(new (Function.bind.apply(Function, [void 0].concat(Object.keys(context), [codeYouWantToExecute])))()).apply(void 0, Object.values(context));
This evaluates expressions in a given context without the need to prefix vars with 'this'.
Has caveats but works fine.
Can also incorporate custom functions in the expression.
Just call an 'evaluate' function, passing in your expression, context and an optional function.
The context must be a flat structure (no nested elements) and the function must be referred to as 'fn' inside the string expression. Answer prints 11 in this case:
var expression = "fn((a+b)*c,2)";
var context = { a: 1, b: 2, c: 3 };
var func = function(x,y){return x+y;};
function evaluate(ex, ctx, fn) {
return eval("var "+JSON.stringify(ctx).replace(/["{}]/gi, "").replace(/:/gi, "=")+", fn="+fn.toString()+";"+ex);
}
var answer = evaluate(expression, context, func);
console.log(answer);
The way variable x
is resolved has nothing to do with context. It is resolved by scope rules: does any of the closures define that variable? If not, look in the global object. At no point the variable is resolved by looking in the context.
You could have a look at this article.
The behaviour is not particular to eval
, it is the same with other functions:
"use strict";
var obj = { x : 3};
var y = 4;
function test() {
console.log(this.x); // 3
console.log(typeof x); // undefined
}
test.call(obj);
function test2() {
console.log(this.y); // 4
console.log(typeof y); // number
}
test2.call(window);