Execution Context in JavaScript

How JavaScript code is executed??

Have you ever wondered how JavaScript works behind the scene?

Let’s understand how JavaScript execution works

What is Execution Context?

When we run any JavaScript, a special environment is created to handle the transformation and execution of the code. This is called execution context.

Execution context is defined as the environment in which the JavaScript code is executed. It acts like a big container that has two components in it.

  • Memory Allocation Phase

  • Code Execution Phase

Memory Allocation Phase: In this phase, all the functions and variables of the JavaScript code get stored as a key-value pair inside the memory component of the execution context. In the case of a function, JavaScript copies the whole function as it is into the memory block but in the case of variables, it assigns undefined as a placeholder. But there is a slight difference between how the memory is allocated to var, let and const.

var: var is allocated memory in the global scope or its function scope and is initialized with undefined.

let and const: let and const are allocated memory in TDZ (Temporal Dead Zone), hence is not accessible or defined while the variable is in TDZ. It remains in TDZ unless assigned a value (initialized).let and const declarations are both block-scoped, which means they are only accessible within the { } surrounding them.

TDZ (Temporal Dead Zone) is the time between hoisting (declaration) and value assignment (initialization).

Code Execution Phase: In this phase, the JavaScript code is executed one line at a time inside the Code Component (also known as the Thread of execution) of the Execution Context.

If there are nested functions, then further Function Execution Contexts are created in Global Execution Context (both declaration and execution phases run again for the nested function).

Note: One important thing to note is that, once a function is returned, it is cleared/removed from the execution context. And it is done with the Call Stack.

Let's understand this with a simple example:

var a = 10;
var b = 20;
function getMultiply(a,b){
    var multiply = a * b
    return multiply
}
var result1 = getMultiply(a,b);
var result2 = getMultiply(16,15);
console.log(result1);
console.log(result2);

Creation phase:

Line 1 – variable a is allocated memory and stores undefined

Line 2 – variable b is allocated memory and stores undefined

Line 3 – getMultiply() function is allocated memory and stores all the code

Line 7 – result1 variable is allocated and stores undefined

Line 8 - result2 variable is allocated and stores undefined

Execution Phase:

Line 1 – places value 10 into variable a

Line 2 - places value 20 into variable b

Line 3 – Skips the function but there is nothing to execute.

Line 7 - Invokes getMultiply() function and creates a new function execution context

Function Creation phase:

Line 4 - Variables a and b are allocated memory and stored “undefined”.

Line 3 - variable multiply is allocated memory and stores “undefined”.

Function Creation phase:

Line 3 - a and b variables are assigned values 10 and 20 respectively.

Line 4 - Calculation is done and 200 is put into result1 variable

Line 5 - Return tells the function to return to the global execution context with the value of result1(200)

Line 6 - Return multiply value is put into the result1 variable

Line 8 - opens another function execution context and repeats the same cycle as above.

You can visualize all this in the below diagram:

To keep the track of all the contexts, including global and functional, the JavaScript engine uses a call stack. A call stack is also known as an 'Execution Context Stack', 'Runtime Stack', or 'Machine Stack'.

A stack is a LIFO (last in first out) data structure. In a stack, we only insert or delete elements at the top of the stack.

Why JavaScript is called a single-threaded language?

There can be only one execution context running at a time, that’s why JavaScript is a single-threaded language.

Whenever an execution context is halted (may be due to some delay or data fetching), a different execution context becomes the current running EC. The halted EC might then at a later point pick back up where it left off.

What is Scope Chain?

Scope in JavaScript is a mechanism that determines how accessible a piece of code is to other parts of the codebase.

Scope answers the questions:

  • From where can a piece of code be accessed?

  • From where can't it be accessed?

  • Who can access it, and who cannot?

Each Function Execution Context creates its scope: the space/environment where the variables and functions it defined can be accessed via a process called Scoping.

This means the position of something within a codebase, that is, where a piece of code is located.

When a function is defined in another function, the inner function has access to the code defined in that of the outer function, and that of its parents. This behavior is called lexical scoping.

However, the outer function does not have access to the code within the inner function.

When execution of the current function is complete, then the JavaScript engine will automatically remove the context from the call stack and it goes back to its parent.

What is Hoisting?

The concept that variables are allocated memory (declared) before the execution of code is known as hoisting.

var is accessible (with undefined as value) before its declaration because it is declared in global memory.

let and const are also hoisted (declared before execution) but in TDZ (temporal dead zone), hence are not accessible before their declaration in the code.

Conclusion

In conclusion, the JavaScript execution context is a crucial part of understanding how JavaScript works behind the scenes. It determines the environment in which code is executed and what variables and functions are available to use.

The creation phase includes creating the global and function execution contexts, creating the scope chain, and allocating memories for the variables and functions. During the execution phase, the JavaScript engine executes the code line by line. This includes evaluating and executing statements.