Execution Context In JavaScript

Execution Context In JavaScript

Flow Of Code Execution Behind The Scenes

How JavaScript Code Gets Executed

For those who don't know, the browser doesn't natively understand the high-level JavaScript code that we write in our applications. It needs to be converted into a format that the browser and our computers can understand – machine code.

While reading through HTML, if the browser encounters JavaScript code to run via a <script> tag or an attribute that contains JavaScript code like onClick, it sends it to its JavaScript engine.

The browser's JavaScript engine then creates a special environment to handle the transformation and execution of this JavaScript code. This environment is known as the Execution Context.

What Is Execution Context

The Execution Context contains the code that's currently running and everything in its execution.

During the Execution Context run-time, the specific code gets parsed by a parser, the variables and functions are stored in memory, executable byte-code gets generated, and the code gets executed.

There are two kinds of Execution Context in JavaScript:

  • Global Execution Context (GEC)

  • Function Execution Context (FEC)

Let's take a look at both one by one.

Global Execution Context (GEC)

Whenever the JavaScript engine receives a script file, it first creates a default Execution Context known as the Global Execution Context (GEC).

The GEC is the base/default Execution Context where all JavaScript code that is not inside of a function gets executed.

For every JavaScript file, there can only be one GEC.

Function Execution Context (FEC)

Whenever a function is called, the JavaScript engine creates a different type of Execution Context known as a Function Execution Context (FEC) within the GEC to evaluate and execute the code within that function.

Since every function call gets its own FEC, there can be more than one FEC in the run-time of a script.

How Does the Execution Context is Created

Now that we are aware of what Execution Contexts are, and the different types available, let's look at how they are created.

The creation of an Execution Context (GEC or FEC) happens in two phases:

  1. Creation Phase

  2. Execution Phase

Creation Phase

In the creation phase, the Execution Context is first associated with an Execution Context Object (ECO). The Execution Context Object stores a lot of important data that the code in the Execution Context uses during its run-time.

The creation phase occurs in 3 stages, during which the properties of the Execution Context Object are defined and set. These stages are:

  1. Creation of the Variable Object (VO)

  2. Creation of the Scope Chain

  3. Setting the value of the this keyword

Let's see each concept in detail.

Creation of the Variable Object (VO)

The Variable Object (VO) is an object-like container created within an Execution Context. It stores the variables and function declarations defined within that Execution Context.

In the GEC, for each variable declared with the var keyword, a property is added to VO that points to that variable and is set to 'undefined'.

Also, for every function declaration, a property is added to the VO, pointing to that function, and that property is stored in memory. This means that all the function declarations will be stored and made accessible inside the VO, even before the code starts running.

Creation of The Scope Chain

After the creation of the Variable Object (VO) comes the creation of the Scope Chain as the next stage in the creation phase of an Execution Context.

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? What can access it, and what can't?

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.

This concept of scope brings up an associated phenomenon in JavaScript called closures. These are when inner functions that always get access to the code associated with the outer functions, even after the execution of the outer functions is complete.

Let's look at some examples to get a better understanding:

Setting The Value of The "this" Keyword

The next and final stage after scoping in the creation phase of an Execution Context is setting the value of the this keyword.

The JavaScript this keyword refers to the scope where an Execution Context belongs.

Once the scope chain is created, the value of 'this' is initialized by the JS engine.

"this" in The Global Context

In the GEC (outside of any function and object), this refers to the global object — which is the window object.

Thus, function declarations and variables initialized with the var keyword gets assigned as properties and methods to the global object – window object.

This means that declaring variables and functions outside of any function, like this:

var role = "Full-Stack Developer"; 

function addOne(x) { 
    console.log(x + 1) 
}

Its exactly the same as:

window.role = "Full-Stack Developer"; 
window.addOne = (x) => { 
console.log(x + 1)
};

Functions and variables in the GEC get attached as methods and properties to the window object. That's why the snippet below will return true.

var name = "Pujari";
name === this.name; // true
"this" in Functions

In the case of the FEC, it doesn't create the this object. Rather, it gets access to the environment it is defined in.

Here that'll be the window object, as the function is defined in the GEC:

var msg = "King Of Cricket-- Virat"; 

function demofunction() { 
    console.log(this.msg); 
} 

demofunction(); // "King Of Cricket-- Virat"

In objects, this keyword doesn't point to the GEC, but to the object itself. Referencing this within an object will be the same as:

theObject.thePropertyOrMethodDefinedInIt;

Consider the code example below:

var msg = "King Of Cricket-- Virat"; 
const Victor = {
    msg: "God Of Cricket-- Sachin", 
    printMsg() { console.log(this.msg) }, 
}; 

Victor.printMsg(); // "King Of Cricket-- Virat"

The code logs "Victor will rule the world!" to the console, and not "I will rule the world!" because in this case, the value of the this keyword the function has access to is that of the object it is defined in, not the global object.

With the value of the this keyword set, all the properties of the Execution Context Object have been defined. Leading to the end of the creation phase, now the JS engine moves on to the execution phase.

Execution Phase

Finally, right after the creation phase of an Execution Context comes the execution phase. This is the stage where the actual code execution begins.

Up until this point, the VO contained variables with the values of undefined. If the code is run at this point it is bound to return errors, as we can't work with undefined values.

At this stage, the JavaScript engine reads the code in the current Execution Context once more, then updates the VO with the actual values of these variables. Then the code is parsed by a parser, gets transpired to executable byte code, and finally gets executed.

Takeaway

JavaScript's Execution Context is the basis for understanding many other fundamental concepts correctly.

The Execution Context (GEC and FEC), and the call stack are the processes carried out under the hood by the JS engine that let our code run.

That's it for the day see you in the next blog until then...

Keep Writing Code❤️