calling eval() in particular context

Javascript

Javascript Problem Overview


I have following javaScript "class":

A = (function() {
   a = function() { eval(...) };
   A.prototype.b = function(arg1, arg2) { /* do something... */};
})();

Now let's assume that in eval() I'm passing string that contains expression calling b with some arguments:

 b("foo", "bar")

But then I get error that b is not defined. So my question is: how to call eval in context of class A?

Javascript Solutions


Solution 1 - Javascript

Actually you can accomplish this with an abstraction via a function:

var context = { a: 1, b: 2, c: 3 };

function example() {
    console.log(this);
}

function evalInContext() {
    console.log(this);        //# .logs `{ a: 1, b: 2, c: 3 }`
    eval("example()");        //# .logs `{ a: 1, b: 2, c: 3 }` inside example()
}

evalInContext.call(context);

So you call the function with the context you want and run eval inside that function.

Oddly, this seems to be working for me locally but not on Plunkr!?

For a succinct (and arguably succulent ;) version you can copy verbatim into your code, use this:

function evalInContext(js, context) {
    //# Return the results of the in-line anonymous function we .call with the passed context
    return function() { return eval(js); }.call(context);
}

EDIT: Don't confuse this and "scope".

//# Throws an error as `this` is missing
console.log(evalInContext('x==3', { x : 3}))

//# Works as `this` is prefixed
console.log(evalInContext('this.x==3', { x : 3})) 

While one could do this:

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);
}

to bridge the gap, it's not what OP's question asked and it uses with, and as MDN says:

> Use of the with statement is not recommended, as it may be the source > of confusing bugs and compatibility issues. See the "Ambiguity Contra" > paragraph in the "Description" section below for details.

But it does work and isn't too "bad" (whatever that means), so long as one is aware of the oddnessess that can arise from such a call.

Solution 2 - Javascript

How to call eval in a given context? 3 words. Use a closure.

var result = function(str){
  return eval(str);
}.call(context,somestring);

Bam.

Solution 3 - Javascript

##Edit Even though, eval.call and eval.apply do not force the context to be passed in correctly, you can use a closure to force eval to execute in the required context as mentioned in the answers of @Campbeln and @user3751385

##My original answer

This is not possible. Eval is called only in the local context(is used directly) or in the global context (even if you use eval.call).

For example,

a = {};
eval.call(a, "console.log(this);"); //prints out window, not a

For more information, look at this great article here

Solution 4 - Javascript

definitely not the right answer, and please do not use with statement, unless you know what you're doing, but for the curious, you can do this

Example

    var a = {b: "foo"};
    with(a) {
        // prints "foo"
        console.log(eval("b"));  
        
        // however, "this.b" prints undefined
        console.log(eval("this.b"));
    
        // because "this" is still the window for eval
        // console.log(eval("this")); // prints window

// if you want to fix, you need to wrap with a function, as the main answer pointed out
        (function(){
	         console.log(eval("this.b")); // prints foo
        }).call(a);     
    }
    
    // so if you want to support both    
    with (a) {
    	(function (){
        console.log("--fix--");
      	console.log(eval("b")); // foo
        console.log(eval("this.b")); // foo
      }).call(a);
    }

with is the failed attempt to create block scopes within functions, kind of what the ES6's let is designed to do. (but not exactly, open and read the resource links)

Solution 5 - Javascript

Here is an article which discussing running eval() in different contexts:

http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context

Usually you do it with eval.call() or eval.apply().

Here is also information regarding eval() and its use cases:

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/eval

Solution 6 - Javascript

So here we are in 2020 and I had this requirement and unfortunately most of the answers are simply not fit for the task.

You can probably skip this part but to explain the situation... here's a more complete answer.

Calling eval is bad... And here I am trying to do exactly that.. Not because I want but because someone forced me to do that... my other option would be to compile extract and AST and eventually evaluate the context of a value... But eval is exactly the tool for the task besides its own evil nature...

So here we go, the mdn documentation:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval

In short, eval used to work and now it's made more difficult to use as the context of evaluation will become the global context so in some case it's impossible to get this. And for some reasons, it didn't look like it could get the this as evaluation context using the answers from this question. So reading further we reach the second part of the article..

Second part of the articles starts with "Never use eval()!", message received! Reading further we come to this:

> Fortunately, there's a very good alternative to eval(): simply using > window.Function().

Ok good!

So looking at the code that seamed very good... Most of the example simply create a function call it and throw it away. This is pretty much how eval works so you could probably do that too...

But for me, the eval context can be reused and potentially quite often so I came up with this.

function create_context_function_template(eval_string, context) {
  return `
  return function (context) {
    "use strict";
    ${Object.keys(context).length > 0
      ? `let ${Object.keys(context).map((key) => ` ${key} = context['${key}']`)};`
      : ``
    }
    return ${eval_string};
  }                                                                                                                   
  `
}

This compiles a function that receives a context to be evaluated given a specific context. It's useful for cases where you know that the evaluation context will always contains a certain set of keys...

The first line of the function generate a list of declared variables on the local scope by taking values from the context passed as a parameter.

This will render JS code that look like this given a context: {a: 1, b: 2}

let a = context['a'], b = context['b'];

The second line is the context you want to evaluate so for something like this 'a + b'

It would render the following code:

return a + b

All in all, there is this utility method:

function make_context_evaluator(eval_string, context) {
  let template = create_context_function_template(eval_string, context)
  let functor = Function(template)
  return functor()
}

That simply wrap it up and return the functor we need...

let context = {b: (a, b) => console.log(a, b)}
let evaluator = make_context_evaluator("b('foo', 'bar')", context)
let result = evaluator(context)

The nice thing about it is that if you want to keep using the evaluator as you know you won't be changing the evaluation context much... Then you can save the evaluator and reuse it with different context..

In my case it's to evaluate a context based on some records so the field set is the same but the values are different... So the method can be reused without having to compile more than one method... On the other plus side, it's supposedly faster than using eval and we're not evaling anything. And if the value passed to the Function would try to used thing outside of its scope... it would be less harmful than eval... For example, it has access to the global scope but shouldn't have access to the lexical scope. In other words...You're limited to the global scope, and the this passed to the call argument.

And then if you really wanted to use it as an eval like function you could do this:

function create_context_function_template(eval_string, context) {
  return `
  return function (context) {
    "use strict";
    ${Object.keys(context).length > 0
      ? `let ${Object.keys(context).map((key) => ` ${key} = context['${key}']`)};`
      : ``
    }
    return ${eval_string};
  }                                                                                                                   
  `
}

function make_context_evaluator(eval_string, context) {
  let template = create_context_function_template(eval_string, context)
  let functor = Function(template)
  return functor()
}

function eval_like(text, context={}) {
   let evaluator = make_context_evaluator(text, context)
   return evaluator(context)
}

Here's an example of how to use:

< eval_like("fun + 2", {fun: 1})
> 3

Or you could even pass just an object like this.

> this.fun = 1
> eval_like("fun + 2", this)
> 3

You can pass expressions like a function declaration but...

> eval_like("() => fun", {fun: 2})()

It will work but it's important that everything that needs to be evaluated is in the context. If the context value aren't there, Javascript will try to take them from the global objects if I'm not mistaken.

So in order to work properly you have to be certain that no variable in the code is taken from the local scope.

Solution 7 - Javascript

This solved my problem.

 function evalInContext(js, context) {
	return function(str){
  		return eval(str);
	}.call(context, ' with(this) { ' + js + ' } ');
}

for implementation similar to "Dom-if"

<template if="{{varInContext == ''}}"> ... </template>

Example

var myCtx = {table: 'Product', alias: 'ProductView'};
evalInContext(' table == "" ', myCtx); //#false
evalInContext(' alias == "ProductView" ', myCtx); //#true

Solution 8 - Javascript

var evalWithinContext = function(context, code)
{
    (function(code) { eval(code); }).apply(context, [code]);
};
evalWithinContext(anyContext, anyString);

Solution 9 - Javascript

You can use my library https://github.com/marverix/meval .

const meval = require('meval');

console.log(
  meval('item.a + item.b * 5', { item: { a: 2, b: 3 } })
);

// outputs: 17

Solution 10 - Javascript

Folks, I think that I have the definitive answer. It works on both JavaScript (Browsers) and NodeJs.

function evalInContext(Context,toEval){
  return eval(`(function Main(){${toEval}})`).call(Context);
}

var context = {a:42,b:82,c:103};
var toEval = "return this";

console.log(evalInContext(context,toEval));//{"a": 42, "b": 82, "c": 103}

Tested on Node v12.16.1, Node v14.7.0, Firefox v79.0 and Google Chrome v84.0.4147.105

Solution 11 - Javascript

Another Bam!

eval('(function (data) {'+code+'})').call(selector,some_data);

This example will keep yours context and send some data. In my case, is a some DOM selector

Solution 12 - Javascript

I was struggling with this for a while in Angular, and found this answer the most useful. I was trying to implement some existing code which use 'with', not allowed by strict. My 'knife and fork' solution to not having to use 'this.' inside the expression I wanted to evaluate, and avoiding 'with' and 'eval' was:

let evalInContext = function (js, context) {
    let keys = Object.keys(context);
    //let code = 'function it(){';
    let code = '';
    for (let i = 0; i < keys.length; i++){
        code += 'let '+keys[i]+' = window._evalincontextobj.'+keys[i]+';\n';
    }
    code += 'return (';
    code += js;
    code += ')';//}\n return it();';
    window['_evalincontextobj'] = context;
    let res = Function(code)();
    console.log(js+' = '+res);
    delete window['_evalincontextobj'];
    return res;
}

This is working for expressions like (watched === 'hello') where 'watched' is a member of context.

Solution 13 - Javascript

What worked for me was using Function constructor and calling it in specified context:

var o = {
  x: 10
}

(new Function(`console.log(this.x)`)).call(o);

This somehow doesn't work in browser's console but works elsewhere.

Solution 14 - Javascript

Includes context plus a function plus an expression. 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:

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);

Solution 15 - Javascript

For node.js env you can use safe-eval to evaluate in context, eg this (taken from https://www.npmjs.com/package/safe-eval):

// your own context API - access to Node's process object and a custom function
var code = '{pid: process.pid, apple: a()}'
var context = {
  process: process,
  a: function () { return 'APPLE' }
}
var evaluated = safeEval(code, context) // { pid: 16987, apple: 'APPLE' }

Solution 16 - Javascript

This seems partly a duplicate of https://stackoverflow.com/q/9781285/8443777 So I figured I'd cross-post my answer in case it's helpful for anyone looking specifically for setting the scope of an eval.

My version of the Function constructor based solution with modern syntactic sugar and what I think is a good solution to avoid polluting the scope with the internal variable containing the evaluated text. (By sacrificing the this context, on which properties can be deleted even inside use strict; code)

(if you want to soft-block/remove-from-scope certain global objects/methods, you can just add them to the scope as undefined and they'll be overridden (never use this for untrusted code, there's always ways around it))

class ScopedEval {
	/** @param {Record<string,unknown>} scope */
	constructor(scope) {
		this.scope = scope;
	}
	eval(__script) {
		return new Function(...Object.keys(this.scope),`
				return eval(
					'"use strict";delete this.__script;' 
					+ this.__script
				);
			`.replace(/[\n\t]|  +/g,'')
		).bind({__script})(...Object.values(this.scope));
	}
}

Personally I prefer being able to separate when I add or adjust the scope and when I eval some code, so this could be used like below.

const context = {
  hi: 12,
  x: () => 'this is x',
  get xGet() {
    return 'this is the xGet getter'
  }
};

const x = new ScopedEval(context)
console.log(x.eval('"hi = " + hi'));
console.log(x.eval(`
let k = x();
"x() returns " + k
`));
console.log(x.eval('xGet'));

x.scope.someId = 42;
console.log(x.eval('(() => someId)()'))

<script>
  class ScopedEval {
    /** @param {Record<string,unknown>} scope */
    constructor(scope) {
      this.scope = scope;
    }
    eval(__script) {
      return new Function(...Object.keys(this.scope), `
				return eval(
					'"use strict";delete this.__script;' 
					+ this.__script
				);
			`.replace(/\t/, ''))
        .bind({
          __script
        })
        (...Object.values(this.scope));
    }
  }
</script>

Solution 17 - Javascript

To calling "eval" in particular context use "call" method:

var myscript = 'console.dir(this)';
context1 = document.querySelector('body');
context2 = document.querySelector('div');

// This execute myscript in context1=body
eval('(function(){'+ myscript +'})').call(context1);

// This execute myscript in context2=div
eval('(function(){' + myscript + '})').call(context2);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmnowotkaView Question on Stackoverflow
Solution 1 - JavascriptCampbelnView Answer on Stackoverflow
Solution 2 - Javascriptuser3751385View Answer on Stackoverflow
Solution 3 - JavascripteverconfusedGuyView Answer on Stackoverflow
Solution 4 - JavascriptbentaelView Answer on Stackoverflow
Solution 5 - JavascriptMikko OhtamaaView Answer on Stackoverflow
Solution 6 - JavascriptLoïc Faure-LacroixView Answer on Stackoverflow
Solution 7 - JavascriptIlario JuniorView Answer on Stackoverflow
Solution 8 - JavascriptEigendreaView Answer on Stackoverflow
Solution 9 - JavascriptmarverixView Answer on Stackoverflow
Solution 10 - Javascriptuser14027420View Answer on Stackoverflow
Solution 11 - JavascriptArturView Answer on Stackoverflow
Solution 12 - JavascriptSimon HView Answer on Stackoverflow
Solution 13 - JavascriptRytis AleknaView Answer on Stackoverflow
Solution 14 - JavascriptMoika TurnsView Answer on Stackoverflow
Solution 15 - JavascriptigorludiView Answer on Stackoverflow
Solution 16 - JavascriptjoshhemphillView Answer on Stackoverflow
Solution 17 - JavascriptDmitry ShashurovView Answer on Stackoverflow