Home > OS >  JavaScript - execute eval in the caller's scope
JavaScript - execute eval in the caller's scope

Time:08-10

eval() executes a string, and does it in the current scope. But what, if I need to make my own eval-like function, which will modify given string of code (not necessary synchronously), and then execute it? I obviously should use eval in it, except I can't, because eval will use scope of my function, instead of someone's who will call my function. Is there a way to force eval to use the caller's scope?

In this simple example I want to make function eval2, which will behave exactly like eval. Code which will "clone" any other single-param function, doesn't work for eval.

function eval2(code){
    return eval(code)
}

(() => {
    let x = 1;
    let code = `console.log(x)`
    eval(code)      // 1
    eval2(code)     // ReferenceError: x is not defined
})()

The only way I see to perform this, is to always put second parameter as callback, which will call eval for the given code. Even so it will not be posible to create local variables (reading is enough for my purposes). Also, of course, it looks terrible, and I would be glad, if there is a way to make same function, which will be nicer to use.
Awful method:

function eval2(code, callback){
    // ... do something with code
    callback(code)
}

(() => {
    let x = 1;
    let code = `console.log(x)`
    eval2(code, code => eval(code))
})()

PS:
From everything I found, the most related to my problem is this: JavaScript extending the eval() function?
But suggested solution there only allows to save/read from the global object, and doesn't allow to read locals.
Anyway I'm wondering why binding eval to something changes its behavior.

CodePudding user response:

Is there a way to force eval to use the caller's scope?

No, there's not, and that's for good reason. eval should be used sparingly, and when it is, it must be called directly under the name eval in non-"strict mode" to be allowed to create new values in the surrounding scope. If you do that, it doesn't count as "nice to use".

Your only option will be to call your function, return the generated code, and have the caller pass it to eval themselves:

eval(myFunction`console.log(x)`);
eval(await myFunction('console.log(x)'));

That way, both the programmer and the engine will be able to immediately see what's going on.

CodePudding user response:

You could wrap your code in an IIFE to which you provide the parameters.

let code = `console.log(x, message)`;
let x = 1;
let message = "test";

let wrappedCode = `((x, message) => { ${code} })(${x}, "${message}");`

eval(wrappedCode);

  • Related