Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
880 views
in Technique[技术] by (71.8m points)

Javascript scoping change

I read a lot about javascript scoping but still can't seem to understand why this code (this line the left arrow points to) changing geval scope to be the global scope and not the function scope. geval is invoked inside test2 so I thought it would have the same scope..

const test2 = () => {
  var x = 2, y = 4;
  console.log(eval('x + y'));  // Direct call, uses local scope, result is 6
  var geval = eval; // equivalent to calling eval in the global scope
  console.log(geval('x + y')); // Indirect call, uses global scope, throws ReferenceError because `x` is undefined
  (0, eval)('x + y'); // another example of Indirect call
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...