[Solved] Javascript scoping change


The following excerpts are taken from ECMA-262 7th edition (ECMAScript 2016) – section numbers sometimes differ between versions.

18.2.1.1 Runtime Semantics: PerformEval( x, evalRealm, strictCaller, direct)


9. If direct is true, then

a. Let lexEnv be NewDeclarativeEnvironment(ctx’s LexicalEnvironment).

b. Let varEnv be ctx’s VariableEnvironment.

10. Else,

a. Let lexEnv be NewDeclarativeEnvironment(evalRealm.[[GlobalEnv]]).

b. Let varEnv be evalRealm.[[GlobalEnv]].

So for an indirect call (not calling eval by name) you arrive at step 10 which calls NewDeclarativeEnvironment(E) with the global environment record as parameter value.

NewDeclarativeEnvironment is described in section 8.1.2.2. As expected this creates an environment record for any variables defined using let within script parsed by eval, and sets the “outer lexical environment reference” of that record to the environment record provided as argument.

Step 10.b sets the variable environment record, for named functions and variables declared with var, to the global environment record of the realm eval was called in – meaning window in a browser document.

In short, calling eval indirectly creates a separate environment record for let variables declared within the code being evaluated, whose next outer scope (lexical reference) is the global object, but uses the global object for var and named function declarations.

If you want to have evaluated code inherit the scope of surrounding code, make a direct call using the name eval as the function reference.

The presence of both 9.a and 10.a means that variables declared with let are not retained after a call to eval no matter what the type of call..


The Historical Why (edit)

The behavior of indirect calls is likely the result of deprecating calling eval on an object and removing the eval property from Object.prototype.

From the JavaScript 1.2 reference for Object data type methods:

eval       Evaluates a string of JavaScript code in the context of the specified object.

and calling eval on an object in such early versions of JavaScript would result in code evaluation as if it were inside a with (object) {} statement.

The rules for indirect calls replace this behavior with a standard response: if an object method is set to eval the previous behavior of object.eval is not recreated and the scope of evaluated code is the global object exceot for let variables. This is similar to the way creating a function from text with new Function behaves as if it were defined in global scope. It also has the effect that the this value seen by indirect calls to eval is the global object (this values reside in environmental records).

solved Javascript scoping change