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.