What is the 'Execution Context' in JavaScript exactly?

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:

  1. Global object - window
  2. Special Object this
  3. 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 would like to address

  1. Context
  2. this context (relation with context)
  3. 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


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 -

  1. Global Object
  2. 'this'
  3. Outer environment
  4. 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.

Tags:

Javascript