3

So, I've been reading some things about closures, scopes, the lexical environment and the execution context. From my understanding the current execution context has a lexical environment and a variable environment (which seems to be a specific type of lexical environment from reading other threads on stack overflow and which I find mentioned in ECMAScript 2016 but not defined). The variable environment has the identifiers that were declared with var and the values associated with them. The lexical environment has, somewhere in its record, the bindings and the values of the variables declared with let and const. I have some questions related to this, however.

Is the variable environment just a variable environment record in the lexical environment (and for some reason the record part has fallen out) or is it a separate lexical environment that has an outer lexical environment reference to something(the outer variable environment or the outer lexical environment)?

Also, where do closures and scopes fit in this picture? From what I understand scope is just what is accessible by the current execution context but is it just the idea of what is accessible or does the definition of scope also include the way JavaScript searches for a variable, looking into the current lexical environment and variable environment and the subsequent environments on the scope chain? About closures, MDN defines a closure as "the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment)" and I see it used several times in ECMAScript 2016 - are closures just the beginning of the new execution context since they determine the lexical environment (what about the variable environment) and they "contain" the function?

Finally, where are variables with let and const stored in the current execution context? I saw in a few Stack Overflow threads that they are in the lexical environment (from searching in Google I couldn't find anything) - specifically, the first answer here (Where are global let variables stored?) says that "Because you used let rather than var, it gets stored in the declarative environment record associated with the global lexical environment object, rather than on the global object itself." and I assume it's the same in general. What about this situation:

function foo() {
    let a = 1;
    {
        let a = 2;
        console.log(a)
    }
    console.log(a)
}

foo()

What happens here - the two variables named "a" cannot be in the same lexical environment, right? So there are two lexical environments, but how is the second one created. Is a new execution context created at the beginning of the new block? Does that happen for every block or only ones that have new variables declared with let and const?

Sorry for all the questions, but my main one is the last one and I do not understand the basis for understanding it, which is why I asked all the questions. Thank you for your time.

asharpharp
  • 169
  • 1
  • 9

1 Answers1

2

A few questions here, really.

MDN defines closure as:

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

Hopefully the "in other words" section here helps with what is meant by "lexical environment."

By contrast, let and const are scoped to the block. From MDN:

let allows you to declare variables that are limited to a scope of a block statement, or expression on which it is used, unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope. ...

So it's basically the same, except the scope is block-level instead of function-level.

The difference between "global lexical environment object" and "global object"

Global object basically means the global variable gets added as a property to the window object. Note the difference in this sample (assuming this code is run in the global scope, not in a function):

let t = 4;
console.log(t, window.t); // window.t will be undefined
// --> 4, undefined

var q = 4;
console.log(q, window.q); // window.q will equal the global q
// --> 4, 4

the two variables named "a" cannot be in the same lexical environment?

*Edit: after doing some more reading, I discovered what I thought I knew about lexical environments wasn't very accurate. Looking at the ecma-262 specs, this seems to shed some light on the subject:

A Lexical Environment consists of an Environment Record and a possibly null reference to an outer Lexical Environment. Usually a Lexical Environment is associated with some specific syntactic structure of ECMAScript code such as a FunctionDeclaration, a BlockStatement, or a Catch clause of a TryStatement and a new Lexical Environment is created each time such code is evaluated.

So it seems relatively certain that the block in your example is getting its own lexical environment, with a reference/pointer to the outer lexical environment of the function body, and so forth.

The section of the ecma specs talking about let and const also makes an interesting distinction:

The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable’s LexicalBinding is evaluated.

Variable Environment

In looking at the spec section on Execution Contexts, It shows that the Execution Context includes a component called VariableEnvironment:

Identifies the Lexical Environment whose EnvironmentRecord holds bindings created by VariableStatements within this execution context.

So it's a second Lexical Environment, which has a specific purpose. To figure out what that special purpose is, let's see what the Environment Record is:

Declarative Environment Records are used to define the effect of ECMAScript language syntactic elements such as FunctionDeclarations, VariableDeclarations, and Catch clauses that directly associate identifier bindings with ECMAScript language values. Object Environment Records are used to define the effect of ECMAScript elements such as WithStatement that associate identifier bindings with the properties of some object.

Several things seem to leap out. It talks about catch clauses and the with statement.

  • Catch clauses seem to be a strange exception, discussed in the spec, annex B.3.5 VariableStatements in Catch blocks. This discusses modifications to behavior of variable declarations.
  • With statements are also odd ducks, since they allow you to reference properties of an object as though they were local variables.

I'm guessing a little here, but it looks to me like this VariableEnvironment property of the Execution Context is a second lexical environment intended to deal with weird things that sort of act like local variables but really aren't.

David784
  • 7,031
  • 2
  • 22
  • 29
  • Thank you for the extensive answer. I could not understand the analogy completely, however. The lexical environment contains a reference to the outer lexical environment and an environment record, right? This existence of references to outer lexical environments creates a scope chain which is one kind of "nesting" or "leveling". Are you saying that the lexical environment also has levels inside it. In my example, there is the global lexical environment and the one of foo which are chained. Does foo have two "levels" here, the inner one denoted by {}? – asharpharp Apr 25 '20 at 22:03
  • Or are you talking about scope chaining in your address analogy? Because, from what I understand, that would require separate lexical environments. – asharpharp Apr 25 '20 at 22:11
  • The use of lexical environment in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let seem to suggest that a new lexical environment is created inside curly brackets (at least sometimes): However, it's important to point out that a block nested inside a case clause will create a new block scoped lexical environment, which will not produce the redeclaration errors shown above. In the same line, the if block's foo has already been created in the lexical environment, but has not yet reached (and terminated) its initialization (which is part of the statement itself). – asharpharp Apr 25 '20 at 22:12
  • In this image: https://pasteboard.co/J5vDnSe.png 1 is the global lexical environment, 2 is the lexical environment used by foo, but what is three? Is it just a part of 2 or a new lexical environment with a reference to 2? – asharpharp Apr 25 '20 at 23:13
  • @asharpharp: Edited my answer. Hopefully it's better now. – David784 Apr 26 '20 at 00:08
  • Thank you. Made everything much clearer. My only remaining question is what the variable environment is. – asharpharp Apr 26 '20 at 07:54
  • @asharpharp I edited again to add some notes on variable environment. I've included all the references I could find in the spec, along with basically my own guess at what it all means. – David784 Apr 26 '20 at 15:00