最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript eval in context without using this keyword - Stack Overflow

programmeradmin2浏览0评论

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. Not tried other browsers. The code I am using is the following:

function evalInContext(js, context) {
    return function() { return eval(js); }.call(context);
}


console.log(evalInContext('x==3', { x : 3})) // Throws
console.log(evalInContext('this.x==3', { x : 3})) // OK

However I expected the first call to evalInContext not to throw. Any ideas why this might be happening?

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. Not tried other browsers. The code I am using is the following:

function evalInContext(js, context) {
    return function() { return eval(js); }.call(context);
}


console.log(evalInContext('x==3', { x : 3})) // Throws
console.log(evalInContext('this.x==3', { x : 3})) // OK

However I expected the first call to evalInContext not to throw. Any ideas why this might be happening?

Share Improve this question edited May 23, 2017 at 12:09 CommunityBot 11 silver badge asked Oct 18, 2016 at 12:10 JoeJoe 1,5352 gold badges20 silver badges39 bronze badges 3
  • 1 Unfortunately the linked answer is wrong. No idea how it got 26 upvotes. – Bergi Commented Oct 18, 2016 at 13:10
  • Because it works @Bergi see answer below... this !== "scope" – Campbeln Commented Sep 6, 2019 at 18:08
  • @Campbeln I never confused this with variable scope. The first snippet in your answer still doesn't produce the result that's shown in the comments. Logging this inside the example function is a different thing than using this right in the evaled code. – Bergi Commented Sep 6, 2019 at 18:54
Add a comment  | 

5 Answers 5

Reset to default 11

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

Scope and Context are not the same

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

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

cannot comment so i post, Joe's answer is great but if the string holds multiple statements (like x++; y++; x+y;) the y++ part would never be executed because it returns at first statement.

with this modification all statements will be executed and the last statement will be the return value.

function evalInScopeAndContext(scr, scopeAndContext)
{
    // execute script in private context
    return (new Function( "with(this) { return eval('" + scr + "'); }")).call(scopeAndContext);
}

i also changed the parameter name because that parameter is used both as scope and context, and they are different things.

Warning 1:

When using this function this.x will equal to x and cannot be changed, because the this property of the scope will be shadowed if present.

if you execute evalInScopeAndContext('this', {a:1, b:2, this:3}) the answer will be {a:1, b:2, this:3} instead of 3.

To access the property this, you have to write this.this instead, i don't think it can be fixed.

Warning 2:

Outer scope (From the caller of this function up to window) can be accessed and modified unless shadowed by the scope object.

So if you plan to execute something like location = 'house'; but forget to insert the location key inside the scope object, you will execute window.location = 'house' and might cause damage.

Instead this.location = 'house'; is always safe because this always exists and will just add the location key to the scope object provided, so you might want to always use this. as a prefix

发布评论

评论列表(0)

  1. 暂无评论