ECMAScript: Where can we find specification about accessibility of let/const variables - javascript

Within the ECMAScript specification, where can we find a clear specification on why let and const are not accessible outside of Lexical Environments created with BlockStatements (as opposed to variables declared with var)?
If BlockStatements now create new lexical environments, then let and const declarations should not create variables accessible outside that lexical environment, but var variables should. I am trying to understand where exactly that behaviour is specified in the latest ECMAScript specification.
From 13.3.1 Let and Const Declarations:
let and const declarations define variables that are scoped to the running execution context's LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable's LexicalBinding is evaluated.
From 13.3.2 Variable Statement:
A var statement declares variables that are scoped to the running execution context's VariableEnvironment. Var variables are created when their containing Lexical Environment is instantiated and are initialized to undefined when created.
As seen, both variable declarations create variables when their containing Lexical Environment is instantiated. Which, in the case of a BlockStatement is when the compiler enters the block.
From 8.3 Execution Contexts:
LexicalEnvironment and VariableEnvironment components of an execution context are always Lexical Environments

As you saw in the description of var, it is scoped to the running execution context's VariableEnvironment. There is a top level VariableEnvironment and then a new one is created when you enter a function and then in this part about Execution Contexts, it says this:
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.
So, at the start of a function, the LexicalEnvironment and VariableEnvironment are one and the same.
Then, down in 13.2.13 Runtime Semantics: Evaluation Block: { }, you can see that a new LexicalEnvironment is created when you enter the block and the prior one is restored when you leave the block. But, there is no mention of a new VariableEnvironment when you enter or leave a block (because that stays constant within the function).
So, since let and const are scoped to the LexicalEnvironment in which they are declared and that is local to a block, it would not be accessible outside the block.
But, var is scoped to the VariableEnvironment which is only created and scoped to the whole function, not to a block.
let and const variables can't be accessed outside their LexicalEnvironment because their definitions are not in the scope hierarchy as soon as the execution context leaves their block (as soon as you leave the block, their LexicalEnvironment is essentially popped off the stack and is no longer in the scope search chain for the interpreter to find variables).
When the specification adds this:
The [let and const] variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable's LexicalBinding is evaluated.
This means that you are denied access to them even in their own Lexical Environment until their definition is evaluated. In layman's terms, this means they are not hoisted to the top of their scope like var is and therefore cannot be used until after their definition. This is implemented by not initializing a let or const variable in the LexicalEnvironment until its statement runs and the GetBindingValue() operation which looks up a variable will see that it is not yet initialized and will throw a ReferenceError. var variables are initialized immediately to undefined so they do not cause this ReferenceError.
You can see how this works in this code:
let x = 3;
function test() {
x = 1;
let x = 2;
console.log("hello");
}
test();
At the let x = 3 line a variable x is initialized in the outer LexicalEnvironment.
Then, when you call test(), at the start of that function, a new LexicalEnvironment is created, the new declaration for x in this block is placed into that new LexicalEnvironment, but not yet initialized.
Then, you get to the x = 1 statement. The interpreter looks up x, finds it in the current LexicalEnvironment, but it is uninitialized, so it throws a ReferenceError.
Per the question in your comment:
I've been turning the spec upside down, but have a hard time spotting that VariableEnvironments are only created for functions. Could you perhaps add an answer showing what steps in the spec you follow to reach said conclusion?
You have to just go through all the places in the spec where a VariableEnvironment is created and you will find that the only places this happens is the beginning of a function execution and at the top level.
For example, here's one on those places: PrepareForOrdinaryCall. There are a few others.
But, no place does it ever describe this happening for the start of a block, only the start of a function.
The way these specifications are written, they describe when things do happen, not when they don't happen (which makes some logical sense), but it means to prove something doesn't happen, you have to fail to find anywhere that says it does happen.

Related

Javascript Closures - How does Execution Context, Variable/LexicalEnvironment, and Scope Chain fit together to form a Closure?

Regarding VariableEnvironment & LexicalEnvironment, it seems that:
Every function call/execution creates a new execution context, and with it entirely new VariableEnvironment & LexicalEnvironment object instances to represent the code/variables inside that function. (For this post, I’ll shorthand group these two into Var/LexEnvironment)
These new Var/LexEnvironment objects also contain references to already-created Var/LexEnvironment objects representing the function object’s outer scope. This is the scope chain.
VariableEnvironment holds function scope variable state.
LexicalEnvironment holds block { } scope variable state.
Full Closure Process Example:
Say we have two functions: function0 which contains function1.
When function1 is called, a new execution context is created = creates entirely new Var/LexicalEnvironment instances are created from code inside function1:
Only one VariableEnvironment instance, and one LexicalEnvironment instance for each* block { }.
The existing lexical environments (outer) are simply pointed to. (This is what's referred to as 'the scope chain'). Run the function0 that contains function1’s outer lexical environments, and you’ll get completely new instances of outer Var/LexEnvironments.
But function1’s references to outer lexical environments don’t change. They don’t become these new Var/LexEnvironments instances.
A function’s [[Scopes]] property = a representation of the Var/Lexical environments that pertain to this function object = a browser's representation of the [[Environment]] property defined in the ECMA spec.
You can view this by using console.dir(funcName) in browser console.
What's incorrect here? What am I missing?
Tl;dr:
It seems safe to think of everything in JS code as being a property of an execution context (either a global or function or eval() execution context). Why?
every execution context has unique lexical/var environments as properties. (New run = new ex. context = new instances of variableEnv objects = new variables with new references)
and these lexical/var environments contain all your variables (identifier-value mappings).
function function0(sizevar) {
s = sizevar * 2
return function function1() {
console.log(s)
console.log(sizevar);
};
}
var size12 = function0(12);
var size14 = function0(14);
So from the above^, when you return an embedded function1, you’re returning a reference to a function instance/object that’s a property of one specific execution context’s lexical/variable environment.
And when function0() returns function1(), scope chain is tied to an execution context’s state (i.e. its variableEnv), even if that execution context is done executing.

Block-level variable redeclaration with var vs. let/const

Part 1
Given this example:
var number = 10
{
var number = 42
}
console.log(number) // 42
Why does line 4 not throw an Uncaught SyntaxError: Identifier 'number' has already been declared? It works with let / const because of block scoping (although the output is, of course, 10 not 42), but how come it works with var?
Part 2
Compare this to the following, which works with var:
var number = 10
var number = 42
console.log(number) // 42
but doesn't with let:
let number = 10
let number = 42 // SyntaxError
console.log(number)
Why is var given a "free pass"? Does it have to do with the number property being reassigned to the window object when var is used?
You are allowed to redeclare var variables in JavaScript, even in strict mode.
The scope of a variable declared with var is its current execution context, which is either the enclosing function or, for variables declared outside any function, global. If you re-declare a JavaScript variable, it will not lose its value.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting
'use strict'
var someVar = 'Hello';
var someVar = 2 + 2;
console.log(someVar);
Why does line 4 not throw an Uncaught SyntaxError: Identifier 'number' has already been declared?
As Sébastien already mentioned, variables declared with var are declared in the current execution context and can be redeclared. The concept of an execution context can be compared to a box. Per the ECMAScript Language Specification Section 8.3:
8.3 Execution Contexts
An execution context is a specification device that is used to track the runtime evaluation of code by an ECMAScript implementation. At any point in time, there is at most one execution context per agent that is actually executing code. This is known as the agent's running execution context.
[...]
Execution contexts for ECMAScript code have the additional state components listed in Table 22.
Table 22: Additional State Components for ECMAScript Code Execution Contexts
Component Purpose
LexicalEnvironment Identifies the Lexical Environment used to resolve identifier references made by code within this execution context.
VariableEnvironment Identifies the Lexical Environment whose EnvironmentRecord holds bindings created by VariableStatements within this execution context.
So everytime you write JavaScript code, it's separated into small individual "boxes" called execution contexts that are created whenever the interpreter encounters a new syntactic structure such as a block, function declaration, etc. In each of these boxes, there are many components, but in particular the LexicalEnvironment and VariableEnvironment. These are both "mini-boxes" inside the parent execution context "box" that hold references to the variables declared inside the current execution context that the code may access. LexicalEnvironment refers to the variables declared with let and const. VariableEnvironment refers to variables created with var.
Now looking at Section 13.3.2:
13.3.2 Variable Statement
NOTE A var statement declares variables that are scoped to the running execution context's VariableEnvironment. Var variables are created when their containing Lexical Environment is instantiated and are initialized to undefined when created. Within the scope of any VariableEnvironment a common BindingIdentifier may appear in more than one VariableDeclaration but those declarations collectively define only one variable.
The last part of the quoted note states the reason why you can declared a var more than once. Every time the interpreter encounters a function, a new VariableEnvironment is created because var is function-scoped, and if you're in the global scope there is one global VariableEnvironment. In your case, you've declared number both times in the global scope because { … } is a block, not a function, but they collectively only define number once. So, your code actually performs the same as this:
var number = 10 //declared once
{
number = 42 //since { … } does not create a new VariableEnvironment, number is the same
//variable as the one outside the block. Thus, the two declarations only define
//number once and every redeclaraction is essentially reassignment.
}
console.log(number) //42
And as you said, let and const are block-scoped. They do not throw an error because { … } creates a new block.
Why is var given a "free pass"? Does it have to do with the number property being reassigned to the window object when var is used?
As described before, a var declaraction can occur many times within a VariableEnvironment - but the same doesn't hole true for let and const. As Bergi mentioned, the ECMAScript authors hadn't realized the downfalls and quirks of having such a bad declarator var until much later, and changing var's behavior would cause the whole internet to collapse, as backwards-compatibility is a huge aspect of ECMAScript/JavaScript. Thus, the authors introduced new declarators, let and const that would aim to be block-scoped and predictable, more like other declarators you see in other languages. As such, let and const declarations throw an error whenever there is another declaration within the same scope. This has nothing to do with window, just because of compatibility and historical issues.

How is block scope managed in the lexical environment?

If I declare a variable in a function using var then a slot for that variable is added to the LexicalEnvironment defined by that function.
function() {
var foo;
}
In the above code the LexicalEnvironment associated with the function contains a slot with a key foo and a value of undefined.
If I use a block-scoped declaration, how is the surrounding LexicalEnvironment affected?
function() {
{
let foo; // How does this affect the LexicalEnvironment?
}
}
function() {
var foo;
}
As you mentioned, foo is available in the LexicalEnvironment is global to all inner functions within that function.
But
function() {
{
let foo; // How does this affect the LexicalEnviroinment?
}
}
Here foo is local to that block alone. It won't be visible outside that block.
How does it affect the LexicalEnvironment ?
If you are referencing, foo anywhere inside that block, the local let foo will override the global var foo which you've defined in that function.
With respect to ES6,
function example(x) {
console.log(y); // ReferenceError: y is not defined
if (x) {
let y = 5;
}
}
Variables declared with a let statement are created as bindings on the lexical environment, rather than the variable environment, of the current execution context. A change to the specification of block statements in ES6 means that each block has its own lexical environment. In the above example, a new lexical environment is created when the block (the body of the if statement) is evaluated. When the let statement is evaluated a binding is added to this lexical environment and is innaccessible from the outer lexical environment (that of the function declaration itself).
Refer
Questions like this are best be answered by looking at the spec:
Block : { StatementList }
Let oldEnv be the running execution context’s LexicalEnvironment.
Let blockEnv be NewDeclarativeEnvironment(oldEnv).
Perform BlockDeclarationInstantiation(StatementList, blockEnv).
Set the running execution context’s LexicalEnvironment to blockEnv.
Let blockValue be the result of evaluating StatementList.
Set the running execution context’s LexicalEnvironment to oldEnv.
Return blockValue.
NOTE: No matter how control leaves the Block the LexicalEnvironment is always restored to its former state.
So what happens here?
When the block is evaluated, a new lexical environment is created, with the current lexical environment as "parent". The new environment replaces the current environment for the duration of evaluating the block.
If I use a block-scoped declaration, how is the surrounding LexicalEnvironment affected?
Other than it is temporarily replaced, it is not affected at all.

VariableEnvironment's functions

Variable/Lexical Environment component of execution context
Question 1:
For what VariableEnvironment component of exection context is needed?
As said in spec LexicalEnvironment component
used to resolve identifier references made by code
within this execution context.
But VariableEnvironment component used to
holds bindings created by VariableStatements and FunctionDeclarations
within this execution context.
Ok, but evaluation of PrimaryExpression:Identifier is execution with LexicalEnvironment component, not VariableEnvironment of running execution context:
Let env be the running execution context‘s LexicalEnvironment.
If the syntactic production that is being evaluated is contained in a strict mode code, then let strict be true, else let strict be false.
Return the result of calling GetIdentifierReference function passing env, Identifier, and strict as arguments.
Thus is VariableEnvironment need for bindings storage only?
Establishing function context is occuring as:
sec. 10.4.3
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.
Where at step 9 bindings add to a VariableEnvironment only. At step 5 [[Scope]] internal property indicates to outer LexicalEnvironment. Thus i have question
Question 2:
Is it true that just after creation of execution context of function code LexicalEnvironement's initial environment record contains the same bindings that environment record of outer LexicalEnvironment?
Consider the following code snippet into the global code?
Example of function code
function bar(){
var b={b:'b'}
var o={o:'o'}
}
bar();
Question 3:
Is it true that just after creation of bar execution context before the start of execution function code VariableEnvironment's environment record will be contain bar-->function bar(){ ... }, b-->{b:'b'}, o--> {o:'o'}, but LexicalEnvironment's environment record
Question 1
VariableEnvironment is the leaf of the scope chain at the start of a function. Since
all variables and functions are hoisted at the top of the current function (or global scope), it defines the scope of identifiers visible strictly inside a given function.
LexicalEnvironment extends VariableEnvironment when you use the constructs with and catch. See my other answer for more details.
Both environments are used for name resolution. LexicalEnvironment is used most of the time, except in function bodies (i.e. a function within a function), where VariableEnvironment (of the parent function) is supposed to be used instead.
As I said in my previous answer, the case is rather unfrequent and various implementations behave differently.
I'm not sure I understand what you mean by 'binding storage'.
Bindings take place when applicable (i.e parameters passing or assignment) once the names are resolved, whichever environment allowed the resolution.
Basically, the names inside a VariableEnvironment are those of local variables (bound to heap objects when the variables are assigned) and parameters (bound to the passed values when the function is called, as described in steps 8-9 of spec 10.4.3).
The names in LexicalEnvironment will be either the name of the exception context for a catch statement or the names of an object's properties for a with statement. They will be bound to their corresponding instances when the statement is evaluated, and disposed of at the end of the statement body.
For instance:
function stick_to_top (element)
{
with (element.style) { top = 0; }
}
stick_to_top (document.getElementById ("wanderer"));
binding of top will occur when the statement top = 0; is executed, during function invokation.
Name resolution will resolve top as element.top using the Lexical context of stick_to_top() enhanced by the with statement that will contain the names of all element.style properties, and resolve element as stick_to_top()'s argument using the Variable context of stick_to_top(), which contains only the name of the parameter element.
In the end, value 0 will be assigned to property style.top of the DOM object having the ID "wanderer" (assuming getElementById() found it, of course).
Now if you're up for a joke, try this :
var tip = { style:{top:1}};
var top = { style:'', invaluable_information:42};
var tap = { };
stick_to_top (tip);
stick_to_top (top);
console.log ("tip %o top %o", tip, top);
stick_to_top (tap);
result:
tip [object Object] top 0
SCRIPT5007: Object expected
tip behaves just like a regular DOM object since we defined a style.top property for it
tap does not have a style property, so tap.style is undefined and the interpreter throws an error when trying to process the with statement.
top is the unlucky one. It does have a style property so the with statement is happly creating an empty Lexical environment. Now identifier top is nowhere to be found inside stick_to_top(), so name resolution goes back to global context where it happily finds an unsuspecting top variable. Inevitable tragedy ensues...
Question 2
Both Lexical and Variable environments are local to a context. The scope is made of a chain of environments, i.e. when resolving a name, if the identifier could not be found in the current environment, parent environments are tried in succession until you reach the toplevel environment where identifiers are considered properties of the global object.
(since you seem to be interrested mostly in variables, I leave aside the function prototype scope for the sake of concision)
So the answer to your question is no. In your example, the lexical environment of the toplevel is the collection of all global variables (i.e. the properties of the window object), while the lexical environment of foo() is identical to its variable environment, containing only the names of the two locals b and o.
Question 3
It will just contain b-->{b:'b'}, o--> {o:'o'}
bar-->function bar(){ ... } will be in the toplevel's lexical context.
the bar identifier will be a synonym of window.bar.
(here again, think of a JavaScript program being enclosed in an implicit with (window) statement).

What is the 'Execution Context' in JavaScript exactly?

My title pretty much sums it all.
Can anyone enlighten me on...
"What is the 'Execution Context' in JavaScript?"
and on how it relates to 'this,' hoisting, prototype chaining, scope and garbage collection?
You're asking about several different concepts that aren't very closely related. I'll try to briefly address each.
Execution context is a concept in the language spec that—in layman's terms—roughly equates to the 'environment' a function executes in; that is, variable scope (and the scope chain, variables in closures from outer scopes), function arguments, and the value of the this object.
The call stack is a collection of execution contexts.
See also this answer and this article.
Scope is literally that: the scope in which a variable can be accessed. Simplistically:
var x;
function a() {
var y;
}
x can be accessed from anywhere. When a is invoked, x will be in the outer scope. (Stored in the scope chain.)
In contrast, y can only be accessed by code in a() because it is limited to a's scope. This is what the var keyword does: restricts a variable to the local scope. If we omitted var, y would end up in the global scope, generally considered a bad thing.
Think of hoisting as more of a compile-time thing. In JavaScript, function declarations are "hoisted" to the top of their scope. In other words, they are parsed and evaluated before any other code. (This is opposed to function expressions, which are evaluated inline.) Consider the following:
a();
b();
function a() { }
var b = function() { }
The call to a() will succeed because its declaration was hoisted to the top; a was assigned to automatically before program execution began. The call to b() will fail with a TypeError because b will not be defined until line 4.
You have asked so many concepts but lets pick one by one & understand them.
The environment in which your code is running is Execution context.
It is created when your code is executed.
Execution Context (Global), created by JS Engine contains 3 important things for you:
Global object - window
Special Object this
Ref to outer environment
Lets see a simple example to understand Global Execution Context:
var a = "Hello World";
function b(){
}
When JS Engine runs this above code it creates following Execution context (shown in image):
Global Execution Context
Now let's see how JS Engine creates Execution Context (then we will dig out & understand hoisting):
consider this scenario:
b();
console.log(a);
var a = "Hello World!";
function b(){
console.log("Called b!");
}
I can call the function b() even though it is declared later.
This means JS Engine is doing something before my code is executed, lets see what:
JS Engine performs following two steps to while executing any code:
CREATION PHASE :
JS Engine parses - run through your code & identifies variables & functions created by code (which will be used in execution phase)
Setup memory space for Variables & Functions - "Hoisting"
Hoisting - before your code is executed, the JS Engine set asides memory space for Var & Func used inside the code.
These variables & functions comprise the Execution Context of any function that is be executed.
All variables in JS are initially set to undefined.
Execution PHASE: pretty simple to understand,
When the code is executed line-by-line (by JS interpreeter) it can
access the variables defined inside Execution Context
variable assignment are done in this phase
A new Execution Context is created whenever function invocation is there
Execution Context Stack:
What happens when you invoke a function:
function b(){
}
function a(){
b();
}
a();
Now first of all Global Execution Context is going to be created
(as explained above)
then execution starts and interpreeter encounters call to function
a(), and here a new execution context is created pushed on top EC
Stack
so anytime you invoke a function a new EC is created & placed on top of EC Stack.
so now EC for a() is CREATED interpreeter will execute the code
inside a() line-by-line
then intrepreeter encounters call to function b(), this creates
another EC which is pushed on top or EC stack
When b() finishes it will be popped-off the stack then a() will finish &
all the way down to Global EC
see Execution Stack for above code snippet
I have addressed only the topics that are most closely related.
Execution Context is the wrapper around your existing code; which contains code that you have not written; but is generated by the JS Engine.
It comprises of the following -
Global Object
'this'
Outer environment
Your code
An Execution Context is created each time you run your .js file/app. The first step in this creation phase is Hoisting. The JS Engine reserves space or set's up memory for all the variables and functions defined in your code. These are then accessed when your code is executed line-by-line.
For example:
b();
console.log(a);
var a = "hi!";
function b() {
console.log("calling function");
}
Here, the function b() and variable a are both accessed before they are defined, however, due to hoisting the console will not throw any error.
The output will look like - (try it)
calling function
undefined
Notice how the function was executed completely, but we have undefined for the variable. This is because Hoisting is performed differently for functions vs variables. The function as a whole is picked up into the memory, but for the variables, space is reserved as a placeholder with the value of undefined. The actual value is then replaced when the engine executes your code line-by-line.
I hope this clears the concept for you.
I would like to address
Context
this context (relation with context)
Scope
1 : Execution Context
JavaScript is a single threaded language, meaning only one task can be executed at a time. When the JavaScript interpreter initially executes code, it first enters into a global execution context by default. Each invocation of a function from this point on will result in the creation of a new execution context.
This is where confusion often sets in, the term execution context is actually for all intents and purposes referring more to scope and not context. It is an unfortunate naming convention, however it is the terminology as defined by the ECMAScript specification, so we’re kinda stuck with it.
Each time a new execution context is created it is appended to the top of the execution stack. The browser will always execute the current execution context that is atop the execution stack. Once completed, it will be removed from the top of the stack and control will return to the execution context below.
An execution context can be divided into a creation and execution phase. In the creation phase, the interpreter will first create a variable object (also called an activation object) that is composed of all the variables, function declarations, and arguments defined inside the execution context. From there the scope chain is initialized next, and the value of this is determined last. Then in the execution phase, code is interpreted and executed.
2 : this Context
What is “this” Context?
Context is most often determined by how a function is invoked. When a function is called as a method of an object, this is set to the object the method is called on:
var obj = {
foo: function() {
return this;
}
};
obj.foo() === obj; // true
The same principle applies when invoking a function with the new operator to create an instance of an object. When invoked in this manner, the value of this within the scope of the function will be set to the newly created instance:
function foo() {
alert(this);
}
foo() // window
new foo() // foo
When called as an unbound function, this will default to the global context or window object in the browser. However, if the function is executed in strict mode, the context will default to undefined.
3 : Variable scope
A variable can be defined in either local or global scope, which establishes the variables’ accessibility from different scopes during runtime. Any defined global variable, meaning any variable declared outside of a function body will live throughout runtime and can be accessed and altered in any scope. Local variables exist only within the function body of which they are defined and will have a different scope for every call of that function. There it is subject for value assignment, retrieval, and manipulation only within that call and is not accessible outside of that scope.
ECMAScript 6 (ES6/ES2015) introduced the let and const keywords that support the declaration of block scope local variables. This means the variable will be confined to the scope of a block that it is defined in, such as an if statement or for loop and will not be accessible outside of the opening and closing curly braces of the block. This is contrary to var declarations which are accessible outside blocks they are defined in. The difference between let and const is that a const declaration is, as the name implies, constant - a read-only reference to a value. This does not mean the value is immutable, just that the variable identifier cannot be reassigned.
For Other topics:
GC : GC
Prototyping : Prototyping
The "Execution Context" is an umbrella that wraps all of the code to help manage it. It's like a manager that manages any environment. Since there's so many lexical environments because in a JavaScript application you have lots of variables and functions you need a way to manage everything. What comes first, what comes second and so on, and if you did not have an "Execution Context" environment everything goes to hell. So consider the "Execution Context" a wrapper, a manager that manages your code.
When you execute a function you create a new execution context comprising a local memory which is called a variable environment and this which is a placeholder, it will refer inside its execution context to whatever is the left of the . where that function is being called upon.
what is EC(Execution context) in JS ?
Simply put, an execution context is an abstract concept of an
environment where the Javascript code is evaluated and executed.
Whenever any code is run in JavaScript, it’s run inside an execution
context.
For more depth details : https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0
I suppose a simple example would explain everything.
Note: function.call(object) calls function function in context of object
// HTML
​<p id="p"></p>​​​​​​​​​​​​​​​​​​​​​​​​
// JavaScript
function helloWorld() {
alert("HelloWorld: " + this);
}
var p = document.getElementById("p");
helloWorld(); // HelloWorld: [object DOMWindow]
helloWorld.call(window); // HelloWorld: [object DOMWindow]
​​​​​​helloWorld.call("String"); // HelloWorld: String // Note: "toString()"
helloWorld.call​​​​(p); // HelloWorld: [object HTMLParagraphElement]
helloWorld.call(new Date());​​​​ // HelloWorld: Tue Feb 21 2012 21:45:17 GMT+0100 (Central Europe Standard Time)
Execution Context is a wrapper to help manage the code that is running.
In your code you will see lots of lexical environments (which means areas of the code between { ... }) but which one is currently running is managed via execution contexts. It can contain your code and it also can contain beyond what you've written in your code.

Categories

Resources