TL;DR: Inside a function, [[VariableEnvironment]]
and [[LexicalEnvironment]]
both point to the same Environment Record, whose [[outerEnv]]
points to the Environment Record inside which the function was created.
The [[VariableEnvironment]]
internal property of an Execution Context points to the corresponding function scope, that is, the scope in which var
and function declarations create their Bindings.
On the other hand, [[LexicalEnvironment]]
points to the current block scope. That is where Bindings created by let
, const
, class
, catch
and with
statements exist, and from where Binding lookups happen.
A new [[LexicalEnvironment]]
is generated for each block, while there's only one [[VariableEnvironment]]
created for an Execution Context.
On the topmost scope of functions (which have their own Execution Contexts), these two are the same, and indeed, they point to the same Environment Record. You can see this in PrepareForOrdinaryCall, which is evaluated before a function is called:
The abstract operation PrepareForOrdinaryCall takes arguments F (a function object)
and newTarget (an Object or undefined). It performs the following
steps when called:
- Let callerContext be the running execution context.
- Let calleeContext be a new ECMAScript code execution context.
- Set the Function of calleeContext to F.
- Let calleeRealm be F.[[Realm]].
- Set the Realm of calleeContext to calleeRealm.
- Set the ScriptOrModule of calleeContext to F.[[ScriptOrModule]].
- Let localEnv be NewFunctionEnvironment(F, newTarget).
- Set the LexicalEnvironment of calleeContext to localEnv.
- Set the VariableEnvironment of calleeContext to localEnv.
- Set the PrivateEnvironment of calleeContext to F.[[PrivateEnvironment]].
- If callerContext is not already suspended, suspend callerContext.
- Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
- NOTE: Any exception objects produced after this point are associated with calleeRealm.
- Return calleeContext.
Emphasis mine
The [[outerEnv]]
property of that Environment Record points to the [[Environment]]
of the function, which is the Environment Record where the function was constructed.
The [[outerEnv]]
of an Environment Record that is the [[LexicalEnvironment]]
of an inner block, points to the Environment Record of the block it's inside of.
So, in your case, it would look like this:
+-----------------------------+
| fun Execution Context |
+-----------------------------+
| | +----------------------+
| [[LexicalEnvironment]] -----------> | Environment Record |
| | +----------------------+
| [[VariableEnvironment]] ----------> | |
| | | bindings: [ |
+-----------------------------+ | a = 1, |
| b = 2 |
| ] |
| [[outerEnv]] --+ |
| | |
+-----------------|----+
+-----------------------------+ |
| Global Execution Context | |
+-----------------------------+ v
| | +----------------------+
| [[LexicalEnvironment]] -----------> | Environment Record |
| | +----------------------+
| [[VariableEnvironment]] ----------> | |
| | | bindings: [ |
+-----------------------------+ | fun = <Function> |
| ] |
| [[outerEnv]] --+ |
| | |
+-----------------|----+
|
v
null