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
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.
Sometimes I read "invocation context" sometimes "execution context". I would like to know if we are talking about the same concept.
I must say that in ECMAScript6 specification I don't find any reference to the "invocation context".
The two terms are closely related but not the same thing.
In a nutshell they define scope vs. context. Scope is about the environment that code runs in (kind of like a room - - it's about where the code is) and context is about an actual object that caused some code to be executed (like who was responsible for putting you in that room).
An "execution context" refers to the "scope chain" that is in
effect when some code is running. A scope chain is a list of
memory locations that should be checked (in a particular order) for
identifiers (variable, constant and function names) to be resolved to
a value. Since JavaScript is executed in a single-threaded
environment, only one task can be executed at a time. The currently
executing code (and its associated scope) define the execution
context.
A simple example can be shown like this:
// This area is in the Global execution context (scope) because the code is
// not wrapped in a function or any other kind of code block.
var x = "Global";
// "Global" is the result because the JavaScript engine will always look
// in the current scope for a declaration for the identifier in question.
// It will find a declaration for "x" right here in the Global scope, so
// that's the value it will use.
console.log(x);
var y = "Also Global";
function parent(){
// This area is in the "parent" execution context (scope)
var x = "parent";
// "parent" is the result (not "Global") because when this function is
// executing, its scope is the most accessible. The JavaScript engine
// looks here first to find out what "x" is. This is known as variable
// "hiding" because the x in the parent scope hides the x in the Global scope.
console.log(x);
function child() {
// This area is in the "child" execution context (scope)
var x = "child";
// "child" is the result (not "Global" or "parent") because when this
// function is executing, its scope is the most accessible. The
// JavaScript engine looks here first to find out what "x" is. This
// x now hides the x in parent, which is hiding the x in Global.
console.log(x);
// "Also Global" is the result here. First the current execution
// context (scope) is checked for a "y" variable. There isn't one,
// so the next scope in the scope chain (function parent) is checked.
// There is no "y" declared there either. So, again, the next highest
// scope in the chain (Global) is checked and that is where "y" is
// found, so the value of that "y" is used.
console.log(y);
// Here, we will get "undefined". All the scopes in the chain will
// be checked and if we go all the way up to Global and still don't
// find a declaration for "z", there is no other scope to look in.
console.log(z);
}
child();
}
parent();
The three execution contexts (scopes) shown above can be entered into in various ways. Those various ways give rise to the "invocation context".
An "invocation context" can also more accurately be called an
"invocation context object" which refers to the object that was
used to invoke some code. This may seem like the same thing as the
"execution context", but the "invocation context" refers to the
object that leads to code executing and that executing code is doing so
within its own execution context (scope).
The biggest differentiating point between these two terms is understanding that the invocation context leads to the binding of the keyword this to an object for the duration of the execution context. this binding is volatile in JavaScript and the object that this binds to is subject to change as you enter into a new execution context. For all intents and purposes, this is the object that invoked some code, or this is the "invocation context".
I've written another answer that goes into more detail about how to determine what this will bind to and you can see that here.
See the following snippet for an explanation of invocation context. It illustrates one execution context (function foo), but two invocation contexts (the button and Global).
function foo() {
// When this function is "invoked" via the button click
// the invocation context is the button and the console will
// log this as: [object HTMLButtonElement]
// But, when the function is invoked from a direct call to foo
// from the Global execution context, this will be bound
// to the window object. So, in that case we'll get: [object Window]
console.log("The 'this' object (invocation context object) is: " + this);
}
// Call foo from the Global execution context.
foo();
var btn = document.getElementById("btnTest");
// When the button is clicked, execute foo
btn.addEventListener("click", foo);
<button id="btnTest">Click Me</button>
"execution context" is the official term (i.e. used by the specification) for a stack frame. As the wording says, it provides the context for the (current) execution of code, which basically consists of internal evaluation state, the function (code) that is currently evaluated, and the active variable scope. Being part of a stack, it knows where to return to when the function execution ends.
"invocation context" probably refers to the context object on which a method is invoked, i.e. the obj in obj.method(). It will become the value for this in that function. The spec doesn't use this term anywhere, what you'll find are thisArgument, thisValue and receiver. It's referred to as ThisBinding in the environment record that is part of the lexical environment (scope) that is contained in every execution context.
Sometimes I read "invocation context" sometimes "execution context". I would like to know if we are talking about the same concept.
I must say that in ECMAScript6 specification I don't find any reference to the "invocation context".
The two terms are closely related but not the same thing.
In a nutshell they define scope vs. context. Scope is about the environment that code runs in (kind of like a room - - it's about where the code is) and context is about an actual object that caused some code to be executed (like who was responsible for putting you in that room).
An "execution context" refers to the "scope chain" that is in
effect when some code is running. A scope chain is a list of
memory locations that should be checked (in a particular order) for
identifiers (variable, constant and function names) to be resolved to
a value. Since JavaScript is executed in a single-threaded
environment, only one task can be executed at a time. The currently
executing code (and its associated scope) define the execution
context.
A simple example can be shown like this:
// This area is in the Global execution context (scope) because the code is
// not wrapped in a function or any other kind of code block.
var x = "Global";
// "Global" is the result because the JavaScript engine will always look
// in the current scope for a declaration for the identifier in question.
// It will find a declaration for "x" right here in the Global scope, so
// that's the value it will use.
console.log(x);
var y = "Also Global";
function parent(){
// This area is in the "parent" execution context (scope)
var x = "parent";
// "parent" is the result (not "Global") because when this function is
// executing, its scope is the most accessible. The JavaScript engine
// looks here first to find out what "x" is. This is known as variable
// "hiding" because the x in the parent scope hides the x in the Global scope.
console.log(x);
function child() {
// This area is in the "child" execution context (scope)
var x = "child";
// "child" is the result (not "Global" or "parent") because when this
// function is executing, its scope is the most accessible. The
// JavaScript engine looks here first to find out what "x" is. This
// x now hides the x in parent, which is hiding the x in Global.
console.log(x);
// "Also Global" is the result here. First the current execution
// context (scope) is checked for a "y" variable. There isn't one,
// so the next scope in the scope chain (function parent) is checked.
// There is no "y" declared there either. So, again, the next highest
// scope in the chain (Global) is checked and that is where "y" is
// found, so the value of that "y" is used.
console.log(y);
// Here, we will get "undefined". All the scopes in the chain will
// be checked and if we go all the way up to Global and still don't
// find a declaration for "z", there is no other scope to look in.
console.log(z);
}
child();
}
parent();
The three execution contexts (scopes) shown above can be entered into in various ways. Those various ways give rise to the "invocation context".
An "invocation context" can also more accurately be called an
"invocation context object" which refers to the object that was
used to invoke some code. This may seem like the same thing as the
"execution context", but the "invocation context" refers to the
object that leads to code executing and that executing code is doing so
within its own execution context (scope).
The biggest differentiating point between these two terms is understanding that the invocation context leads to the binding of the keyword this to an object for the duration of the execution context. this binding is volatile in JavaScript and the object that this binds to is subject to change as you enter into a new execution context. For all intents and purposes, this is the object that invoked some code, or this is the "invocation context".
I've written another answer that goes into more detail about how to determine what this will bind to and you can see that here.
See the following snippet for an explanation of invocation context. It illustrates one execution context (function foo), but two invocation contexts (the button and Global).
function foo() {
// When this function is "invoked" via the button click
// the invocation context is the button and the console will
// log this as: [object HTMLButtonElement]
// But, when the function is invoked from a direct call to foo
// from the Global execution context, this will be bound
// to the window object. So, in that case we'll get: [object Window]
console.log("The 'this' object (invocation context object) is: " + this);
}
// Call foo from the Global execution context.
foo();
var btn = document.getElementById("btnTest");
// When the button is clicked, execute foo
btn.addEventListener("click", foo);
<button id="btnTest">Click Me</button>
"execution context" is the official term (i.e. used by the specification) for a stack frame. As the wording says, it provides the context for the (current) execution of code, which basically consists of internal evaluation state, the function (code) that is currently evaluated, and the active variable scope. Being part of a stack, it knows where to return to when the function execution ends.
"invocation context" probably refers to the context object on which a method is invoked, i.e. the obj in obj.method(). It will become the value for this in that function. The spec doesn't use this term anywhere, what you'll find are thisArgument, thisValue and receiver. It's referred to as ThisBinding in the environment record that is part of the lexical environment (scope) that is contained in every execution context.
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.
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?