Could someone clarify what the difference is between these two, as they exist in the Execution context? It's hard for me to read the ECMA 262 v 5 specification and clearly see the difference.
Thank You,
Both are components (of the same type) of Execution Contexts, but they serve distinct purposes (from the spec):
LexicalEnvironment
Identifies the Lexical Environment used to resolve identifier references made by code within this execution context.
VariableEnvironment
Identifies the Lexical Environment whose environment record holds bindings created by VariableStatements and FunctionDeclarations within this execution context.
The next paragraph explains why they need to be different:
When an execution context is created its LexicalEnvironment and VariableEnvironment components initially have the same value. The value of the VariableEnvironment component never changes while the value of the LexicalEnvironment component may change during execution of code within an execution context.
That does not happen often and usually both refer to the same Lexical Environment. A good example for a changing LexicalEnvironment is given in the question Why do catch clauses have their own lexical environment? - see §12.14. The other place I could find in the spec where this happens are With Statements (§12.10) where an Object Environment Record is dynamically used for the identifier resolution - yet variable/function declarations are static.
As far as I understand, those are just different names used to refer to the same type of entity (Lexical Environment). They have different names due to different purposes.
LexicalEnvironment is used to resolve identifiers while VariableEnvironment is used to declare variables and functions.
Both of them reference Lexical Environment (= Environment Record + optional outer Lexical Environment; aka scope chain) that's created for each execution context.
The LexicalEnvironment and VariableEnvironment components of an
execution context are always Lexical Environments. When an execution
context is created its LexicalEnvironment and VariableEnvironment
components initially have the same value. The value of the
VariableEnvironment component never changes while the value of the
LexicalEnvironment component may change during execution of code
within an execution context
Example in pseudo code:
// VariableEnvironment (global) = { __outer__: null }
// LexicalEnvironment = VariableEnvironment (global)
(function foo() {
// VariableEnvironment (A) = { x: undefined, __outer__: global }
// LexicalEnvironment = VariableEnvironment (A)
var x;
(function bar(){
// VariableEnvironment (B) = { y: undefined, __outer__: A }
// LexicalEnvironment = VariableEnvironment (B)
var y;
x = 2;
// VariableEnvironment (A) = { x: 2, __outer__: global }
// LexicalEnvironment is still the same as VariableEnvironment (B)
})();
})();
Related
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 a LexicalEnvironment), and when invoking a function, a new LexicalEnvironment 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:
function handleInvokeFunction(func) {
const localEnv = new EnvironmentRecord();
localEnv.set('[[OuterEnv]]', func['[[Environment]]'])
calleeContext.lexicalEnvironment = localEnv;
}
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].
Environment Record is a specification type used to define the association of Identifiers to specific variables and functions.
Each time such code is evaluated, a new Environment Record is created to record the identifier bindings that are created by that code.
One interesting thing about Environment Records, is that they are responsible for allowing access to the parent variables, such in:
// Environment Record "EnvA"
const hello = "world";
if (1) {
// Environment Record "EnvB"
console.log(hello);
}
// outputs: world
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].
Every Environment Record has an [[OuterEnv]] field, which is either null or a reference to an outer Environment Record.
Each time the runtime encounters a new block of code, it performs the following steps [src]:
Create a new Environment Record.
Set the [[OuterEnv]] field of that new environment to the old (currently active) environment.
Return the new 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).
An execution context is a specification device that is used to track the runtime evaluation of code by an ECMAScript implementation.
The execution context stack is used to track execution contexts.
Execution contexts have a LexicalEnvironment component. It is needed to keep track of the variables in that specific block of code.
LexicalEnvironment: Identifies the Environment Record used to resolve identifier references made by code within this execution context. [src]
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].
The running execution context is always the top element of this stack.
To expand on the above steps, when entering a new block of code, this is what would actually happen [src]:
Create a new Environment Record with proper [[OuterEnv]] value (same steps as before).
Use the new Environment Record as the running one.
Evaluate all the lines inside the block.
Restore to the previous Environment Record.
Return the result and exit the block.
Commenting on the previous example, this is what would happen:
// This is Environment Record "EnvA".
// The [[OuterEnv]] field for "EnvA" is null.
// The running context LexicalEnvironment is "EnvA".
const hello = "world";
if (1) {
// Found new block
// Create a new Environment Record "EnvB".
// Set the "EnvB" [[OuterEnv]] field to
// the running context LexicalEnvironment.
// In this case, its "EnvA".
// Change the running context LexicalEnvironment to "EnvB".
// Evaluate all lines in the body using the new
// running context LexicalEnvironment.
// In this case, its "EnvB".
console.log(hello);
// Restore the previous running context LexicalEnvironment.
// Return the result.
}
// The running context LexicalEnvironment is Environment Record "A".
// Since the inner block restored before returning, it didn't change.
[[Environment]]
Still, no mention of functions yet. Which are different because functions can be executed outside the declared scope.
That's where [[Environment]] appears.
[[Environment]]: The Environment Record that the function was closed over. Used as the outer environment when evaluating the code of the function.
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:
// This is Environment Record "EnvA".
// The [[OuterEnv]] field for "EnvA" is null.
// The running context LexicalEnvironment is "EnvA".
const hello = "world";
// Found a function, store the running context
// into its [[Environment]] field, and do nothing else.
function foo() {
// This block runs only after invoking bar().
// Create a new executing context "calleeContext".
// Create a new Environment Record "EnvB".
// Set the "EnvB" [[OuterEnv]] field, to the value
// stored inside [[Environment]]. In this case, its "EnvA".
// Set the LexicalEnvironment of "calleeContext" to "EnvB".
// Push "calleeContext" to the execution context stack.
// That makes "calleeContext" the running execution context.
// Evaluate all lines in the body
// using "calleeContext" LexicalEnvironment.
// In this case, its "EnvB".
// If a function is found here, set its
// [[Environment]] to "calleeContext" LexicalEnvironment.
console.log(hello); // works because `hello` was in "EnvA"
// Pop "calleeContext" from the execution context stack.
// "calleeContext" is no longer the running execution context.
// Return the result.
}
const bar = foo;
bar();
// The [[Environment]] of `bar` is still "EnvA".
// The running context LexicalEnvironment is still "EnvA".
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]] and LexicalEnvironment are Environment Records, they are using for different things.
[[Environment]] holds the LexicalEnvironment where the function was declared.
LexicalEnvironment is a component of execution contexts, which stores information about the variables in that particular block of code.
In the ECMAScript spec, it is stated that:
A LexicalEnvironment :
Identifies the Lexical Environment used to resolve identifier references made by code within this execution context.
Also,
The LexicalEnvironment and VariableEnvironment components of an execution context are always Lexical Environments. When an execution context is created its LexicalEnvironment and VariableEnvironment components initially have the same value.
And that:
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.
The question is, why do we need a VariableEnvironment in first place, since the a LexicalEnvironment seems to be enough to track variable-value bindings?
Now, let's have a look at this code example in JavaScript [Questions in comments] :
var a = 1;
function x(b){
var c = 2;
// During the creation phase of the excution context of the function
// the ifStatement Block will it be skipped?
if(c >= 1){
let d = 3;
// Does e belongs to the Lexical Environment of the IfStatementBlock or to the FunctionDeclaration?
var e = 4;
a = 5;
}
}
VariableEnvironment is used for names that are scoped to the entire function, while LexicalEnvironment is used for names that are scoped to the closest enclosing block (EcmaScript 6's let and const declarations).
In your example, c and e both belong to the VariableEnvironment of the function. var declarations are hoisted to the top of the function, but e won't be initialized unless the if block is entered. So it's equivalent to:
var a = 1;
function x(b) {
var c = 2;
var e;
if (c >= 1) {
let d = 3;
e = 4;
a = 5;
}
}
why do we need a VariableEnvironment in first place, since the a LexcicalEnvironment seems to be enough to track variable-value bindings?
The reason for the VariableEnvironmnet is to ensure that there is a local scope that can be used which does not interfere with the existing scopes. Without a VariableEnvironment, there would be no scoping at all, as the LexicalEnvironment would constantly be altered by executing code.
VariableEnvironment - Identifies the Lexical Environment whose EnvironmentRecord holds bindings created by VariableStatements within this execution context.
This is definitely needed.
During the creation phase of the excution context of the function, the if Statement Block will it be skipped?
No, it will not. if statements are not triggers for new execution contexts, and as a result that scope will be part of the Variable Environment of the function which will hold an inner LexicalEnvironment.
Does e belongs to the Lexical Environment of the IfStatementBlock or to the FunctionDeclaration?
The e variable is hoisted, since it is a var, and as such it is part of the Variable Environment for function x. The d value is part of an inner LexicalEnvironment created as a result of the let declaration inside of an if block.
In JavaScript: Understanding the Weird Parts lexical environment is explained as the scope of your code while execution context is a collection of lexical environments, and that it includes stuff beyond your written code.
The descriptions of these terms still sound overlapping in functionality and it's unclear as to what execution context does or how it does it.
The best way to think of an execution context is as a stack frame, while lexical environments are indeed the scopes.
The respective spec chapters (§8.1 Lexical Environments and §8.3 Execution Contexts) explain:
Execution contexts contain the current evaluation state of code, a reference to the code (function) itself, and possibly references to the current lexical environments.
Execution contexts are managed in a stack.
Lexical environments contain an environment record in which the variables are stored, and a reference to their parent environment (if any).
Lexical environments build a tree structure.
With every change of the execution context, the lexical environment changes as well. However the lexical environment may change independently from that as well, for example when entering a block.
Execution Context & Execution Context stack : Execution context is the internal javascript construct to track execution of a function or the global code. The js engine maintains a stack - execution context stack or call stack, which contains these contexts and the global execution context stays at the bottom of this stack. And a new execution context is created and pushed to the stack when execution of a function begins. A particular execution context tracks the pointer where statement of the corresponding function is being executed. An execution context is popped from the stack when corresponding function's execution is finished.
Lexical Environment : it's the internal js engine construct that holds identifier-variable mapping. (here identifier refers to the name of variables/functions, and variable is the reference to actual object [including function type object] or primitive value). A lexical environment also holds a reference to a parent lexical environment.
Now, for every execution context -- 1) a corresponding lexical environment is created and 2) if any function is created in that execution context, reference to that lexical environment is stored at the internal property ( [[Environment]] ) of that function. So, every function tracks the lexical environment related to the execution context it was created in.
And every lexical environment tracks its parent lexical environment (that of parent execution context). As a result, every function has a chain of lexical environments attached to it. [Note: in js a function is an object, creating a function by a statement means creating an object of type Function. So like other objects, a function can hold properties both internal and user defined]
The js engine search any variable or function identifier in the current lexical environment, if not found it searches the chain attached to the enclosing function. (for global code this chain does not exist). So, you understand how scoping of variables and functions are maintained. The concept of closure is also backed by, this chain (not a standard term, I just used it to ease the understanding). When a function instance is passed to another function as an argument, to be used as callback, it carries it's chain with it (sort of).
Note: the answer is based on what I learned from 'Secrets of the Javascript Ninja, 2/e'
The answer marked above likened the Execution Context to a Stack Frame. But the Execution Context in JavaScript is no ordinary Stack Frame.
In the global Execution Context, JavaScript Engine creates two things for you, a Global Object (an Object is a collection of name/value pairs) and a special variable called 'this'. In browsers, the Global Object is a window object. In NodeJS, the global object is something else. The point is there is always a global object.
When you create variables and functions that are not inside other functions, those variables are in the global context and thus get attached to the global object, which in the case of browsers is the window object.
hello = 'hello world'
> "hello world"
window.hello
> "hello world"
The execution context in JavaScript is created in two phases. The first phase is the Creation Phase. In the global execution context, the Global Object is setup and in memory, the special variable 'this' is setup, points to Global Object and is in memory, and there is an Outer Environment (Lexical Environment). As the Parser begins the Creation Phase of the execution context, it first recognizes where you created variables and functions. So the Parser sets up memory space for variables and functions. This step is called 'Hoisting'. Hence, before your specific code is executed line by line, the JavaScript Engine already set aside memory space for the variables and functions you created in the global Execution Context:
console.log(a);
console.log(b());
console.log(d);
var a = 'a';
function b(){
return 'called from b';
}
function c(){
d = 'd';
}
> undefined
> called from b
> Uncaught ReferenceError: d is not defined
In the above example, since the variable 'a' and the function b() were created in the global Execution Context, memory space is allocated for them. Note though that the variables are not initialized, just declared with an undefined value. This is not the case for functions. Functions are both declared and initialized, so both the identifier and the actual code of the function is stored in memory. Also note that since d (even though it is not specified with var, let or const) is not in the global Execution Context, no memory space is allocated for it. And so an exception is raised when we try to access the d identifier.
Now if we invoke c() before we reference the d variable, then a new execution context is evaluated (which is not the global Execution Context) and then d will be in memory (in that new execution context, the this keyword will point to the global object since we did not place 'new' before the function invocation and so d will be attached to the global object):
console.log(a);
console.log(b);
console.log(c());
console.log(d);
var a = 'a';
function b(){
return 'called from b';
}
function c(){
d = 'd';
return 'called from c';
}
> undefined
> b() { return 'called from b' }
> called from c
> d
One final point about the Creation Phase of the Execution Context. Since 'hoisting' occurs, order of function definitions or variables do not matter in terms of lexical scoping.
The second phase of the Execution Context is called the Execution Phase, and this is where assignments occur. The JavaScript engine begins parsing your code. So that is when variables will be assigned a value. In the first phase, they were just declared and stored in memory with an undefined value. 'undefined' is a placeholder which is JavaScript's way of saying 'I don't know what this value is yet'. This is the same placeholder JavaScript gives when you declare a variable without assigning it a value. Consequently, it is not a good idea to rely on JavaScript's 'Hoisting' feature. Simply put, do not use a variable in global Execution Context (or in any execution context) before it is declared with var, const or let. So it is always better to do this:
var a = 'a';
function b(){
return 'called from b';
}
console.log(a);
console.log(b());
Do not confuse yourself with the JavaScript built-in data type 'undefined' vs an undefined exception raised by the parser. When a variable is not declared anywhere and you try to use it, the JavaScript Engine will raise an exception 'Uncaught ReferenceError: [variable] is not defined'. JavaScript is saying that the variable is not in memory. This is different then initializing a variable with the undefined data type.
In addition to the global Execution Context, function invocation creates a new Execution Context. First, in the below example, a global Execution Context is created. The Creation Phase of the global Execution Context will be handled by the JavaScript Engine. It will create a Global Object (window in the case of browsers), and it will create a special variable 'this' and point it to the Global Object. Then the b and a function will be attached to the Global Object. Memory space will be allocated for them, and since they are functions, their code will be stored in memory as well. If there were any variables, they will be stored in memory too, but they will not be initialized and thus stored with the data type undefined. Then the Execution Phase begins. Since JavaScript is single-threaded, it executes line by line. During this time, it encounters a(). It sees the '()' and knows it must invoke the function a. A new Execution Context is created and placed on the Execution Stack. As you probably know, the stack data structure is last in first out. An Execution Context is pushed onto the Execution Stack, and when it is finished, it is popped from the stack. Whatever context is on top, that is the Execution Context that is currently running.
function b(){
}
function a(){
b();
}
a();
The a() Execution Context will be stacked on top of the global Execution Context. It will have its own memory space for local variables and functions. It will go through the Creation Phase and Execution Phase of the Execution Context. During a() Execution Context's Creation Phase, since it did not declare any variables or functions, it does not allocate space for any new variables or functions. If it did declare any local variables or functions, it will go through the same 'Hoisting' process as is the case in the global Execution Context. Also, a new special 'this' variable is created for that particular function. Note though if you invoke the function without the new keyword, then 'this' will still reference to the global object, which is the window object in browsers.
Then, it moves on to the Execution Phase. Here it invokes the b() function, and now a third Execution Context is created. This is the b() Execution Context. It goes through the same Creation and Execution Phase as the other Execution Contexts'.
When b() finishes, it is popped off the stack, and then when a() finishes, it will be popped off the stack, and then we return back to the global Execution Stack. Importantly, each execution context stores a pointer to where it left off when it invoked a function and hence created a new execution context. So when b() finishes, a() returns to the statement wherein it invoked b(). And then continues execution on the next statement in that execution context. Again, remember, JavaScript is single-threaded, so it executes line by line.
The key about the Lexical Environment is it has a link to any Outer Environment (i.e. its scope chain), so it is used to resolve identifiers outside the current Execution Context. Ultimately, a corresponding Lexical Environment is created for every Execution Context. The Lexical Environment cares about where the code sits physically (lexically) in your application.
Execution Context
Execution Context is created every time, the JS engine calls a function. They stays in the call stack.
Lexical Environment
The lexical environment includes the scopes that are present at a certain point in time. Every execution context has its own lexical environment.
Check the image and see the scope and the call stack section on the right side of the image:
The call Stack section contains two execution context (test1 and anonymous).
The scope section contains the lexical environment of the test1 execution context.
test1 lexical environment has three objects Global, Local, and Block when the JS engine is executing line no 10.
I have global code snippet:
function foo(){
var a={a:'a'};
var b={b:'b'};
}
What is the value of Variable/LexicalEnvironment just after creation of foo's execution context? I think that VariableEnvironment's environment record must contain bindings foo--> function(){ }, a-->{a: 'a'}, b-->{b: 'b'}, but LexicalEnvironment's environment record must contain a global environment's environment record.
An execution context contains a lexical environment, a variable environment and a this binding.
(source)
So, to clarify:
An execution context contains both a lexical environment and a variable environment
They're different things.
Environment contains two types of environment records
declarative environment record - these directly associate an identifier with a language value.
object environment record - these define the effect ecmascript elements (such as with on the properties of some object.
So - to answer your question.
If we check the spec it clearly states:
When an execution context is created its LexicalEnvironment and VariableEnvironment components initially have the same value. The value of the VariableEnvironment component never changes while the value of the LexicalEnvironment component may change during execution of code within an execution context.
So, in your case of entering the function foo, the process is specified in 10.4.3 "entering function code" the VariableEnvironment is:
Let localEnv be the result of calling NewDeclarativeEnvironment passing the value of the [[Scope]] internal property of F as the argument.
Set the LexicalEnvironment to localEnv.
Set the VariableEnvironment to localEnv.
What is [[Scope]]? It's defined when the function is created and is specified in 13.2 Creating function objects.
So, like we said before, the VariableEnvironment and LexicalEnvironment are identical, they both have an empty (no binding) declarative environment record, and an outer lexical environment reference of the environment to the [[scope]]
So, the [[Scope]] internal property of foo is passed, that is detected by the parsing and contains the references to a and b and the function foo itself.
As already established in your previous question, VariableEnvironment and LexicalEnvironment refer to the same environment. But the environment which LexicalEnvironment refers to might change during the execution of the function. From the spec:
When an execution context is created its LexicalEnvironment and VariableEnvironment components initially have the same value. The value of the VariableEnvironment component never changes while the value of the LexicalEnvironment component may change during execution of code within an execution context.
So here is what happens when foo is about to be executed, in JavaScript-ish pseudo code:
// foo.[[scope]] refers to the global lexical environment
localEnv = new NewDeclarativeEnvironment(foo.[[scope]])
LexicalEnvironment = localEnv
VariableEnvironment = localEnv
env = VariableEnvironment.envRec
env.CreateMutableBinding('a', false)
env.SetMutableBinding('a', undefined, false)
env.CreateMutableBinding('b', false)
env.SetMutableBinding('b', undefined, false)
I.e. the environment record of the lexical environment has two entries, a and b. I don't know why it is necessary to have both, LexicalEnvironment and VariableEnvironment, but that's how the language is designed.
In the sec. 10.3 describes the components of execution context as the follwoing:
The LexicalEnvironment and VariableEnvironment components of an
execution context are always Lexical Environments. When an execution
context is created its LexicalEnvironment and VariableEnvironment
components initially have the same value. The value of the
VariableEnvironment component never changes while the value of the
LexicalEnvironment component may change during execution of code
within an execution context.
Ok, let we have code snippet:
{
alert(o.prop);
var o={prop: 'prop'};
}
My understanding of this code snippet:
When control is transferred to this code snippet a corresponding execution context will be created and pushed to stack. This context is become a running execution context (called cont). During the cont is creating the VariableEnvironment of cont is creating. After the cont's creation this code starting to execute.
But as said in sec.10.3
The value of the VariableEnvironment component never changes
Thus we can assume that the VariableEnvironment never changed after creation of cont. I.e. environment record of cont's VariableEnvironment contains all binding created by VariableStatement and FunctionDeclarartion initially. Hence we can apply to object by reference o. But I have TypeError: o is undefined.
Question:
Why TypeError, described above is caused? I'm expected that prop will be displayed with alert message because the value of the VariableEnvironment component never changes hence environment record of the VariableEnvironment never changes, hence all bindings of this record is immutable.
Might I dont understand mean of value of EnvironmentRecord correctly?
First, your code is just definining a block, and JavaScript doesn't have block scope; environment records are associated with global scope and functions, not blocks.
So let's assume that's actually a function so it creates a new scope and environment record:
function foo()
{
alert(o.prop);
var o={prop: 'prop'};
}
That's treated exactly as though it were this:
function foo()
{
var o;
o = undefined;
alert(o.prop);
o={prop: 'prop'};
}
Which should clarify things a bit. :-) You're trying to dereference o as though it referred to an object, but it doesn't (yet); its value is undefined.
This is because all var declarations are processed upon entry to the execution context, and all variables are initialized to undefined, before any step-by-step work is done. Any initializer associated with a var (e.g., var o = ...) is really an assignment, and happens later when that code is reached in the step-by-step execution of the code.
More (on my blog):
Poor misunderstood var