It is told that every block of code has a hidden object called LexicalEnviroment
. That object contains a reference to the outer scope and an EnviromentRecord
, which contains information about the current scope.
On the other hand, it is said that functions are capable of closure thanks to their [[Enviroment]]
construct that "remembers where the function was defined".
I'm confused, what is the relationship between LexicalEnviroment
object and [[Enviroment]]
?
Are they one and the same? Do only functions have [[Enviroment]]
construct? Do they have a LexicalEnviroment
object then?
tl;dr
They are both instances of an
Environment Record
.LexicalEnvironment
is only a component of execution contexts (functions can't have aLexicalEnvironment
), and when invoking a function, a newLexicalEnvironment
is created for the current context, with its[[OuterEnv]]
field is being set to the Function[[Environment]]
field.If it would be Javascript, I guess it would be:
Disclaimer: I'm not an expert on the subject. I just want to give you an overall idea, while waiting for a real expert to chime in here.
Environment Records
Environment Records, for the record (pun intended), hold all information required for the function to be executed. For functions, for instance, they hold the variable declarations, and the
this
value. Of course, this is oversimplified [src].One interesting thing about Environment Records, is that they are responsible for allowing access to the parent variables, such in:
That's because they have a field called
[[OuterEnv]]
, which is pointing to the parent environment. So in the above example, the[[OuterEnv]]
field of "EnvB" is set to "EnvA" [src].Each time the runtime encounters a new block of code, it performs the following steps [src]:
[[OuterEnv]]
field of that new environment to the old (currently active) environment.Execution Context
In order to do this for all the blocks, an execution context stack is used, which is almost like a stack trace [src]. The difference is that instead of only pushing and poping on entering and exiting a function (like a stack trace would do), it will only change the topmost entry on entering or exiting blocks of code (like an if block).
Execution contexts have a
LexicalEnvironment
component. It is needed to keep track of the variables in that specific block of code.LexicalEnvironment
is an Environment Record, so it has an[[OuterEnv]]
field, which is what the runtime will be changing accordingly.LexicalEnvironment
doesn't belong to function objects. It only belongs to a execution context.The running execution context represents the block of code the runtime is currently executing at that moment [src].
To expand on the above steps, when entering a new block of code, this is what would actually happen [src]:
[[OuterEnv]]
value (same steps as before).Commenting on the previous example, this is what would happen:
[[Environment]]
Still, no mention of functions yet. Which are different because functions can be executed outside the declared scope.
That's where
[[Environment]]
appears.When there is a function inside a block, the running
LexicalEnvironment
is stored as the[[Environment]]
field of the function object [step 35][step 3][step 14].When calling that function, the
[[Environment]]
field is used as the[[OuterEnv]]
[step 10].It's like the function stores all the variables it had access inside
[[Environment]]
, and when invoked, it can have access to them again using[[Environment]]
.The other difference with normal blocks, is that in this case, instead of changing the running execution context, a new one is created and pushed to the stack [creation in step 3][push in step 12][pop in step 8].
Now, to try with a simple code:
Since the example is calling the function in the same environment that it was declared, it is not actually using "closures", but you might get the idea already.
In conclusion
Although both
[[Environment]]
andLexicalEnvironment
areEnvironment Records
, they are using for different things.[[Environment]]
holds theLexicalEnvironment
where the function was declared.LexicalEnvironment
is a component of execution contexts, which stores information about the variables in that particular block of code.