7

I faced with strange behaviour when using eval in JS.

var f = function () {
    var x = 10;

    return function () {
        eval('console.log(x);');
        window['eval']('console.log(x);');
    }
};

f()();

OUTPUT:

10
undefined:1
console.log(x);
            ^
ReferenceError: x is not defined

Why using eval explicitly captures the x but global['eval'] doesn't? And even though global['eval'] doesn't capture x, why it's unable to see after eval, which already captured x?

Sergey Sahakyan
  • 723
  • 8
  • 15
  • 5
    the better question is why are you using eval? eval does magical things. it is best to leave it alone. – Dan Sep 17 '16 at 20:08
  • 1
    Sometimes for remote testing I need to quickly load and run my local code. Not for production code :) – Sergey Sahakyan Sep 17 '16 at 20:11
  • http://stackoverflow.com/a/17281213/1005215 – Nehal J Wani Sep 17 '16 at 20:12
  • @NehalJWani it's not related, question is about capturing when you use `eval`. Thanks. – Sergey Sahakyan Sep 17 '16 at 20:15
  • @SergeySahakyan The answer is related to lexical scoping. – Nehal J Wani Sep 17 '16 at 20:16
  • 1
    _"Sometimes for remote testing I need to quickly load and run my local code"_ - then I recommend you find a better way to do that. You have problems understanding what is going on now already, so it is likely that you come across such or similar behavior again when testing this way - so you will never be able to quickly determine whether a problem is with your actual code, or just with the way you are getting it to execute. I'd rather use debugging tools that make my life easier, not harder. – CBroe Sep 17 '16 at 20:40

4 Answers4

7

window['eval'] operates at global scope, eval() operates at local scope.

From Mozilla's Javascript reference:

If you use the eval function indirectly, by invoking it via a reference other than eval, as of ECMAScript 5 it works at global scope rather than local scope; this means, for instance, that function declarations create global functions, and that the code being evaluated doesn't have access to local variables within the scope where it's being called.

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

StvnBrkdll
  • 3,924
  • 1
  • 24
  • 31
3

Your inner function does not actually capture the reference of x, and so it is never directly passed to eval.

eval usually works at the local scope and so the first call succeeds (because the local scope contains the declaration of x).

However, if you invoke eval in such a way that you don't have a direct reference to it, it will invoke itself in the global scope, which var x is not a part of, and it fails.

Just don't use eval.

Dan
  • 10,282
  • 2
  • 37
  • 64
2

You can use Function.prototype.bind() to pass x to returned function

var f = function () {
    var x = 10;

    function y(n) {
        eval(`console.log(${n})`);
        window["eval"](`console.log(${n})`);
    }

    return y.bind(this, x)
};

f()();
guest271314
  • 1
  • 15
  • 104
  • 177
  • 1
    Well, this is string interpolation, you don't even need bind for this, you could just interpolate the variable directly in the eval statement without using a parameter. `f = function() { var x = 5; return function () { eval(\`console.log(${x})\`) } }` – Dan Sep 17 '16 at 20:22
1

window.eval work in global scope.

var variable = 1;

(function(){
  var variable = 100,
      cmd = "++variable";
  document.write(eval(cmd)+"\n"); // increment local var 100 and output 101
  document.write(window.eval(cmd)+"\n"); // increment global var 1 and output 2
})();
Grzesie2k
  • 19
  • 2