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.
Related
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.
function one() {
function two() {
alert(i);
}
return two;
}
(function() {
var i = 3;
var f = one();
f(); // Uncaught ReferenceError: i is not defined
}());
The articles I've read say that, when a function is called, a new execution context is added on to the stack and the scope chain is created by traversing up the stack. But surely that would result in 3 being alerted in the above code?
How does it work out the scope chain?
The scope chain is determined by the lexical nesting of scopes. The scope can be affected dynamically (via with), but that causes a lot of problems. It's definitely not the case that the current stack of function activations has anything to do with scope.
When you do var i = 3;, you are creating a local variable that exists only in the scope of the immediately-invoked function expression (IIFE).
Any functions that are defined in that scope can access the variable i.
one() (and therefore also two()) was declared outside the scope, so it has no idea what i is. It's looking up its scope chain to window.i, which doesn't exist.
The call stack (as mentioned keeps adding an execution context to the top of the stack when a function is invoked) and lexical scope (which allows access to variables from outer scope) are independent of each other.
Lexical scope depends only on where the function is created not where the function is invoked, so in your example function "two" is created inside function "one", so when variable "i" inside function "two" tries to access "i", it tries to find "i" in its own scope first, since there is no "i" defined it will go out to the scope where the function is created in your case its function "one" (not where the function is invoked in anonymous IIFE below) and it couldn't find variable "i" again inside function "one", now since function "one" is defined inside global scope (the default execution context) JS will try to find the value of "i" inside the global scope which results in "undefined".
In order to access variable "i" you need to move function "two" into anonymous IIFE as shown below, function two has outer lexical scope of function "one" which has an outer lexical scope of anonymous IIFE which has i assigned to 3 which is alerted.
(function () {
var i = 3;
function one() {
function two() {
alert(i); // will alert 3
}
return two;
}
var f = one();
f();
}());
Hope this is clear now :)
The call stack and the execution context stack are not the same. A function call adds a level to the call stack, but execution context stacks are not changed. However, the function that you call has a different execution context stack.
Each function scope has its own execution context stack which is determined by where the function is created, it doesn't inherit the execution context stack from the calling code.
If you nest functions in each other and only call the inner functions, like in the first example on the page, the call stack and the execution context stack will happen to correspond.
Execution stack is what happens on hardware (memory), regardless of whatever high level language is running. The scope chain model is managed by specific interpreter to implement programming language features such as functional programming, which itself is another program independent from the one you've written, the sequence of function call stacks via the interpreter is different from the actual order on physical hardware.
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
Consider the following snippet of JavaScript:
function outer() {
var x = 10;
function inner() {
var y = 20;
}
}
Obviously, the variable y is not available in the context of outer. Following through the process in the ES5 spec tells us that this is what happens when we enter the outer execution context:
We enter a new execution context (10.4)
Since it's a function, we follow the steps outlined in Entering Function Code (10.4.3)
Let code be the value of F’s [[Code]] internal property.
The [[Code]] internal property of a function is described in Creating Function Objects (13.2). It refers to the FunctionBody as specified by the grammar (the entire contents of the function):
function Identifier ( FormalParameterListopt ) { FunctionBody }
At this point we enter the Declaration Binding Instantiation (10.5) section and do the following:
Create bindings for any arguments in the formal parameter list of the function
For each function declaration in code:
Create a binding in the current scope between function identifier and a Function object (created as specified in Function Definition (13))
There is nothing to say "remove the function we've just processed from code"
Create a binding for the arguments object
For each variable declaration in code:
Create a binding in the current scope for the variable identifier
My question is why, at this point, are bindings not created for variable declarations from inner functions? It seems like code should still contain the entire source text of the outer function (which would include the source text of the inner function). I'm looking for something in the spec which explains the behaviour.
Updated, to be a bit clearer: I am asking about what happens when we enter the context of the outer function. The inner function is never called, and I don't care about what happens when we return from the outer function. I am purely interested in the process, as defined by the spec, of creating bindings for variable declarations when entering a new execution context.
You have a error in your thinking.
Javascript has function-scope. Thus you have a new execution context for each function, thats true. However, after you return from a function this function context expires. That is the reason why you can't access the variables from the inner function after it has returned. You still are in the execution-context of the outer-function but you can't access the execution-context of the inner function anymore.
Citing the spec:
Every return exits an execution context. A thrown exception may also exit one or more execution contexts.
EDIT: To clarify this further: The body of a function declaration is NOT being processed (see 10.5.5.d), only the function identifier and the arguments are passed to the variable environment.
It's right there in the definition of the term code (§10.1):
Function code is source text that is parsed as part of a FunctionBody. The function code of a particular FunctionBody does not include any source text that is parsed as part of a nested FunctionBody.