Demystifying Javascript Code Execution

Demystifying Javascript Code Execution

Subscribe to my newsletter and never miss my upcoming articles

Javascript is a single threaded, dynamically typed and lexically scoped language. I know this might sound like a jargon to you right know, but let's try to understand this by breaking it down. Single threaded means that Javascript can only execute a single statement in a given time. In other words, Javascript is a synchronous language. Dynamically typed means that the type of the variable purely depends on the type of the value that is stored inside the variable.

var variableOne = 1;
console.log(typeof variableOne) // Prints "number"
variableOne = "Hello";
console.log(typeof variableOne) // Prints "string"

The above code snippet is a clear example on how the type of variable changes based on the value that is being assigned. Lexically scoped is the ability of the child functions to access the variables upto it's parent scope and not vice-versa.

Screenshot 2021-09-07 at 2.08.43 PM.png

The above image is a clear representation of lexical scoping, where Function C can access all the variables present in Function C, Function B, Function A and Global Function; Function B can access all the variables present in Function B, Function A and Global Function; Function A can access all the variables present in Function A and Global Function while Global Function can access only the variables present in the Global Function. This clearly proves that, the child functions can access the scope of the parent functions while, the parent functions cannot access the scope of the child functions.

Before proceeding any further, let me first introduce two important components that are required for understanding the javascript code execution.

Execution Contexts - These are sections which are created whenever a Javascript engine executes a code. There are two execution contexts - Global Execution Context and Function Execution Context. Global Execution Context is the first context which get's created when the engine starts executing the code. This is either called the global() or the main() function. Function Execution Context is the context which get's created whenever it encounters a function invocation statement in the code. Both these contexts have two phases - Creation Phase and Execution Phase. Creation Phase is the phase where Javascript creates a global object - window in the case of browser or global in the case of Node JS, a variable of type ,object, called this, which refers to the global object, and allocate a memory heap which stores all the variables and function declarations, where values of the variables are set to undefined. Creation phase of the Function Execution Context is slightly different. Instead of the global object it creates the arguments object which refers to all the parameters that are passed into the function. Execution phase is the phase where Javascript executes the code line by line. This is also the phase where variables will be assigned with their actual values and executes all the function calls that's present in the code.

Call Stack - This is the section where Javascript manages both Global and Function Execution Context. From the name itself, it is very clear that this section works with the principle of Stack. When Javascript starts executing the code, the first context, which is the Global Execution Context get's pushed into the Stack. After which, whenever it encounters a function invocation, it pushes the Function Execution Context of that function into the stack. Similarly, whenever a function gets completed, the function execution context of that function will be popped from the Stack. Code execution is stopped when the Call Stack is completely empty.

Now let's try to understand the end-to-end flow of the Javascript code execution.

var counter = 0;

function increment() {
  counter++
}

function addNumberToCounter(number) {
  var result = counter + number;
  console.log(result);
}

increment();
addNumberToCounter(5);

First, Javascript engine creates a Global Execution Context and kicks off the Creation Phase.

Screenshot 2021-09-07 at 4.06.41 PM.png

In this phase, Javascript engine parses the code where it identifies all the variables and function declarations and store them in the memory heap where the values of all the variables are set to undefined.

After the creation phase is over, it begins the Execution Phase.

Screenshot 2021-09-07 at 4.58.33 PM.png

Now, the Javascript engine starts executing the code line-by-line. In our case it would start from var counter = 0. When it encounters this statement, it checks whether the variable is present in the memory and set the value to 0 as per the statement. After executing this statement, it moves to the next line which in our case would be the function increment(){...}. Since this is a declaration statement, it skips the entire function block (line numbers 3-6). One of the important thing to note here is that, functions does not get executed until and unless the functions are invoked. Since the next line is also a function declaration, it skips the entire function block (line numbers 7 to 10).

Screenshot 2021-09-07 at 5.41.31 PM.png

When it encounters the identifier increment, it checks if the identifier is present in the memory and executes the function code stored in the memory. The engine identifies function execution when it encounters () after the identifier. When the function is invoked, it creates a Function Execution Context and pushes it to the Call Stack. During the creation phase, the arguments object is created, this variable will reference the global Object (window) and checks whether there are any other declaration statements present in the function code. After this phase, the execution phase gets started. When it encounters the statement counter++, it checks whether the variable is present in the local memory of the Function Execution Context. When it does not finds the variable, it moves one level above, which is the Global Execution Context and searches for the variable counter. When it finds the variable, it grabs the value and does the ++ operation, which increases the current value of the variable by 1. Now, the counter variable will be updated with the new value which is 1.

Screenshot 2021-09-08 at 12.03.52 AM.png

As all the statements in the function are executed, the Function Execution Context of the increment function is popped from the Call Stack and destroyed.

Screenshot 2021-09-08 at 12.06.11 AM.png

Screenshot 2021-09-08 at 12.21.45 AM.png

Now, the engine goes back to the Global Execution Context which is currently at the top of the Call Stack. It moves to the next line and executes the next function invocation (addNumberToCounter(number)) , similar to the increment function. The only difference here is that addNumberToCounter(number) function consists of an argument value 5 which passed to the function. During the creation phase, the arguments object is created which will contain all the parameters passed into the function. In this case, number would be a value inside the arguments object. During the execution phase, it checks for the variable result in the local memory of the function execution context. As it's present, it will evaluate the expression counter + number, where it grabs the value of counter and number from the global and local memory respectively. The evaluated result 6, would be assigned to the result variable present in the local memory. It then moves onto the next line and executes the console.log(result) statement. This prints the value of the result present in the local memory of the function.

Screenshot 2021-09-08 at 12.26.19 AM.png

As soon as the execution phase is over, function execution context (addNumberToCounter(number)) is popped from the stack, destroyed and moves back to the global execution context. As the engine identifies that there are no more lines to be executed, it pops the global execution context from the Call Stack and destroys the execution context.

Thank you

If you feel this article was useful, please do show your love and share this to your fellow beings via your social media who could make maximum use of this. Always feel free to connect with me on twitter, linkedIn or email.

Until we meet again, The Mallu Dev signing off 👋 Cheers 🥂

 
Share this
Proudly part of