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.
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.
As said in sec 10.4.3
The following steps are performed when control enters the execution
context for function code contained in function object F, a caller
provided thisArg, and a caller provided argumentsList:
If the function code is strict code, set the ThisBinding to thisArg.
Else if thisArg is null or undefined, set the ThisBinding to the global object.
Else if Type(thisArg) is not Object, set the ThisBinding to ToObject(thisArg).
Else set the ThisBinding to thisArg.
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.
Let code be the value of F‘s [[Code]] internal property.
Perform Declaration Binding Instantiation using the function code code and argumentsList as described in 10.5.
Consider the following code snippet:
function foo(){
var a={p:'p'};
o={c:'c'};
}
Thus we have the following:
Code of our function isnt a strict code
thisArg is null hence, ThisBinding set to the global object
---
---
I dont understand what bindings will be contains environment record represented by [[Scope]] internal property.
Set the LexicalEnvironment to environment which geted at step 5.
Set the VariableEnvironment to environment which geted at step 5.
Perform declaration binding instatiation.
At step 8 bindings are created in the VariableEnvironment, but not in LexicalEnvironment. But in sec 10.3 said that
When an execution context is created its LexicalEnvironment and
VariableEnvironment components initially have the same value.
Question:
Why just after creation of execution context LexicalEnvironment and VariableEnvironment is still equal in my case above?
I am not sure I understand your question, but this is how I understand sec 10.4.3 :
steps 1 to 4 are dealing with the value of this. Basically, in strict mode, this will be left to a null or undefined value instead of defaulting to the global object (window in case of a browser). This covers the cases when a function is not invoked through usual object or event handler mechanisms.
step 5 to 7 mean that a new naming environment is created each time you enter a function.
It describes the creation of this environment, which is linked to the previous one to form the current name scope.
For each new function, two environments are co-existing.
When a name is resolved, Lexical environment is searched first, then Variable environment. If both searches fail, the process is repeated at the upper level of the environment chain, until the 'catch-all' global scope is encountered. In this scope, all identifiers are handled as properties of the global (window) object. You can picture it as the whole code being enclosed in a with (window) block.
Lexical environment can be seen as a temporary augmentation of the variable scope.
Lexical and Variable environments are functionnally identical until you alter the lexical environment with two specific statements: with or catch. It does not mean they are implemented as identical data structures.
In terms of implementation, you could imagine the lexical environment as an empty list and the variable environment as a list containing all local variable and parameter names.
When a catch or with statement is encountered, the lexical list is populated with new names that will take precedence over the ones stored in the variable list.
catch will simply make its argument available for name resolution (i.e. allow you to reference the exception parameter). No big deal since the new name is just as explicit as a function parameter.
with is a rather more dangerous beast. It will create a new environment with the names of all properties of the object passed as its argument. The scope will consist of the chain of variable environments plus this new lexical environment.
Here the new names available for resolution are 'hidden' inside the object.
For instance:
var a = 'a', b = 'surprise!', o = {a:'a'};
with (o) { a = b; }
console.log (a+" "+b+" "+o.a);
will yield
a surprise! surprise!
a is resolved as o.a since o contains a property named a.
b is not found inside the lexical environment and thus the current variable environment is tried and variable 'b' is found.
This is a pretty dangerous mechanism, because if you believe an object contains a given property while it actually doesn't, you will instead reference a variable outside the current scope.
For instance, a simple typo like this:
with (element.style) {leftt = '10px';}
will set the window.leftt property to '10px', unless you happen to have declared a variable named leftt somewhere in the current scope.
Now if you give silly names like 'i' or 'j' to your object properties, chances are you will clobber a random loop index somewhere up the scope chain while believing you are setting the property of an object.
step 8 describes the parameters binding once the function scope is established. Basically, the parameters are bound with the values and their names are added to the variable environment.
The whole point of keeping two separate environments is that the hoisting mechanism always uses the variable environment chain as a scope.
The idea is that a variable or function should behave as if it had been declared at the top of the current scope block, so for instance a function declared inside a with block should not resolve its names with the object properties exposed by the with statement.
Frankly this is a rather moot point, since ECMA spec does not allow function declarations inside blocks, although most implementations do, with varrying results.
Now for your example:
function foo(){
var a={p:'p'};
o={c:'c'};
}
Your function does not contain any with or catch statements, so the scope chain inside 'foo()' is just a list of two variable environments :
global (a bunch of DOM objects all seen as properties of 'window')
function foo (var a)
once you call foo(),
a will resolve as foo's local variable, will be created as an object with the property p of value 'p' (and garbage collected as soon as you leave foo(), unless you manage to reference it from a persistent variable).
o will not be found in foo's variable environment, so it will be caught by the 'catch-all' global scope and thus resolved as a (new) property of the window object. It will create a window.o.c property with the value 'c'.
Does that answer somewhat to your question?
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
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)
})();
})();
I'm reading the Execution Context / Lexical Environment section of the ECMA 262 5 specification. It states the following: (emphasis added)
A Lexical Environment is a specification type used to define the association of Identifiers to specific variables and functions based upon the lexical nesting structure of ECMAScript code. 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 WithStatement, or a Catch clause of a TryStatement and a new Lexical Environment is created each time such code is evaluated.
I noticed that it says nothing about creating a lexical environment for Function Expressions. Is a lexical environment created for function expressions, or is one created only for function declarations? Am I missing something?
Edit: I notice that function code will have its own execution context, which is why I'm also confused why function expression is not mentioned in the lexical environment section.
Yes, every function gets (§10.4.3) its own ExecutionContext when it is called (§13.2.1). That new context is initialised with a new LexicalEnvironment (created by NewDeclarativeEnvironment, §10.2.2.2), deriving from the [[Scope]] of the function - i.e. the LexicalEnvironment it was declared/"expressed" in (§13).
As #Pointy pointed out, the sentence you stumpled upon does not exhaustively list them: "…some [structure] such as…".
If a name is included in a FunctionExpression, that name becomes a read-only binding that shadows any outer declarations of the same name. But that binding may itself be shadowed by a formal parameter or local declaration within the function. Such a binding for the function name is only created for named FunctionExpressions and not for anonymous FunctionExpressions or FunctionDeclarations. The name binding for a FunctionDeclaration is created in the surrounding VariableEnvironment.
Here is a more detailed explantion referencing the ES5.1 spec:
There is more than one environment record associated with a function object. Whenever a function is called a new DeclarativeEnvironmentRecord is created to contain the local bindings of that particular function invocation. That record becomes both the VariableEnvironment and the initial LexicalEnvironment of the ExecutionContext that is created for that invocation. This is specified in section 10.4.3.
When this environment record is created its "outer environment" is set to the value of the [[Scope]] internal property of the function object that is being called. (line 5, 10.4.3) The outer environment provide the bindings for all non-local declarations. The [[Scope]] is set when a function object is created (see the semantics in section 13 and also 13.2). So, each distinct invocation of a specific function object has a different local environment but all invocations of that function share the same outer [[Scope]].
For most functions, the captured [[Scope]] is simply the LexicalEnvironment of the ExecutionContext that was active when the function is created. However FunctionExpressions that include an Identifier as the function name have an extra DeclarativeEnvironmentRecord insert at the head of its [[Scope]] chain. (see steps 1-3 of the third algorithm in section 13).
This extra environment record is used to capture the binding for the function name given in the FunctionExpression.
An instantiated function has a scope. It doesn't matter whether it was instantiated as part of a function declaration statement or a function instantiation expression.
(It's probably more correct to say that an instantiated function has a scope when it is called, and that every call produces a distinct scope.)