14

This piece of code

eval(`
    let a = 0;
    function f() {}
    function g() { a; }
    console.log(f);
`);

works fine on Firefox 48.0 while causing Uncaught ReferenceError: f is not defined on Google Chrome 52.0.2743.116 (64-bit).

It also works fine on Google Chrome if

  • eval is not used, or
  • the code inside eval is surround with {}, or
  • a is not referenced in g, or
  • let is changed to var, or
  • "use strict" is added before the code

What's happening here?

johnchen902
  • 9,200
  • 1
  • 25
  • 63

3 Answers3

5

Tweaking your example you can see what's happening, and while the command is a bit contradictory, it looks like a bug. Define a as a function and log it instead of f, then have a look at the console. You'll see that a closure was created with a, f and g. Since a is referenced in g, and f and g should be visible to each other, it makes a bit of sense. But eval works in the global scope. So when you try to access them, you get undefined. It's like this closure cannot be accessed from anywhere.

Try:

eval('let a = function(){}; function f() {};function g(){a;};console.dir(a);'); 

You'll see this in the console:

<function scope>
    Closure
        a: function()
        f: function f()
        g: function g()

All your other cases make the situation clearer, and prevent the issue:

  • eval is not used: the scope mismatch is less obvious,
  • the code inside eval is surround with {}: the variables are linked through a Block scope.
  • a is not referenced in g: no need for a closure if the variables aren't linked.
  • let is changed to var: var in the global scope is defined in the global scope. So no Closure needed
  • "use strict" is added before the code: use strict in eval prevents variables to be added to the global scope, so again, "easier" to handle. No mismatch between having let needed to be linked with global functions.
Julien Grégoire
  • 16,045
  • 3
  • 24
  • 51
2
eval(`
    "use strict";
    let a = 0;
    console.log(f);
    function f(){
    }
    function g(){
        a;
    }
`);

Block-scoped declarations (let, const, function, class) not yet supported outside strict mode

strah
  • 6,557
  • 4
  • 30
  • 45
  • 1
    `let` is supported in sloppy mode since Chrome 49.0 according to [MDN](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/let#Browser_compatibility). – johnchen902 Aug 12 '16 at 16:15
1

Looks like it's a novel V8 bug! A more minimal test case is

eval(`
    var f;
    let a;
    ()=>a
`);
f;

Variable-scoped declarations (which includes top-level function declarations) aren't getting properly hoisted out of non-strict eval calls when the call also has a nontrivial lexical declaration.

Bakkot
  • 1,065
  • 7
  • 13