What Happens When You Run a JavaScript Program.
Introduction to Execution Context and Call Stack in JavaScript.
Introduction
Whenever we hear the name JavaScript, the first thing that comes to our mind is web browsers. It is due the fact that JavaScript is primarily a client-side language, meaning it runs on your computer within your browser. However, more recently the introduction of Node.js has allowed JavaScript to also execute code on servers. JavaScript is a light weight, single threaded, asynchronous language which can be used to achieve object-oriented, functional as well as procedural paradigms. It is used on both client side and server side. Where HTML and CSS are languages that give structure and style to web pages, JavaScript is used to add interactivity to the web pages.Now let us try to understand what happens under the hood when we execute our JavaScript program.
Deep dive into Execution Context
Every time we execute our JavaScript code inside a web browser, the first thing that happens is creation of global execution context even if there is no code to execute in the JavaScript file. The execution context is an abstract context that contains information about the environment of the currently executing code. Every bit of our code in JavaScript is executed in an execution context. And whenever a program is run this execution context is pushed into the call stack. Call stack is the place where our stack frames are as our code executes. Basically it is the call stack that maintains the order of execution context throughout the program. According to the Mozilla Developer Network:
A call stack is a mechanism for an interpreter (like the JavaScript interpreter in a web browser) to keep track of its place in a script that calls multiple functions — what function is currently being run and what functions are called from within that function.
Lets us dive deeper to find out how execution context is created and how the code is executed. The execution context contains two components the Memory component(also known as variable environment) and the Code component (also known as the thread of execution).
This is the rough pictorial representation of how the execution context looks like. Within the execution the Memory component is the place where all variables and functions are stored as key/value pairs and the Code component is the place where the code is executed line by line. Now let us try to understand this with the help of code example
var a = 2;
function square(x) {
var result = x * x;
return result ;
}
var square1 = square(a);
var square2 = square(5);
Consider the above lines of code are saved in script.js file. When we execute the file script.js in web browser the JavaScript engine begins its job of creating the global execution context. The execution context is created in two phases:
Creation phase:
During this phase the JavaScript allocates memory to the variables and functions. In our code the when JavaScript engine
1) Encounters the first line var a = 2, it allocates memory to it and assigns value "undefined" to it and moves on to the next line.
2) In the second line the JavaScript engine finds a function square. It allocates the memory to this function and stores the complete code (that is within the function) as its value and moves on to the next line.
3) In the next line the JavaScript engine encounters one more variable square1 so it allocates the memory to it and saves "undefined" as its value.
4) Next in the code is the variable square2. As the JavaScript engine reaches this line it again allocates the memory to it and stores "undefined" as its value.
To understand it better here is the pictorial representation of the creation phase for the above example
Code Execution Phase:
During this phase the code is executed and once again the JavaScript engine goes through the whole program line by line as:
1)JavaScript engine reads the first line var a = 2, thus it goes to the memory component and assigns this value to 'a' which was previously(in creation phase) given a value of undefined. Thus the value of 'a ' now becomes 2.
2) Next it encounters the function,now as the function is already stored with its complete code in creation phase so here JavaScript Engine does nothing and moves on to the nest line.
3)Now the JavaScript engine encounters var square1 = square(a) which is basically the function invocation. As soon as JavaScript engine encounters the function invocation in code execution, it creates a whole new execution context for the function within the global execution context.
As now the execution context creation phase begins for the function. During this phase all parameters, variables and function that are within the function are assigned memory first .The JavaScript engine starts reading the function code line by line as:
1)It encounter the function parameter x , so it assigns the memory to x and stores undefined as its value and moves to the next line.
2)Then it encounter the result variable so assigns memory to it and stores value undefined in it as well. After this the function code is complete so it goes to the code execution phase for this function.
During code execution phase of the function the JavaScript engine goes line by line and as it already has encountered var square1 = sqaure(a)
1)Since it already knows the value of variable ' a ' so it passes the value of 'a' i-e 2 to x thus stores
x : 2 in the memory.
2) Next in the code it encounters the line var result = x x; so it performs the operation 2 2 and stores the value of this operation( 4 ) in the result variable.
3) Next in the code it finds the return statement. As soon as JavaScript engine encounters the return statement it returns to the global execution context and removes the execution context of the current function from call stack and the control is transferred to the step where the function was invoked. In our case it returns to the step var square1 = square(a) and the result is assigned to the square1 variable thus square1 variable now holds the value 4. Let us try to understand this with the pictorial representation now:
4) Next the JavaScript engines finds var square2 = square(5), as this is again a function invocation so the new execution context for function is created and then the steps as mentioned above are carried out. But this time during code execution phase the value of x will be 5 and 'a 'will not be involved. During the function code execution step the JavaScript engine will perform the operation 5 * 5 and store the result in result variable. After again encountering the return statement the execution context for the function will be deleted and the result will be returned to square2 variable and the control will transfer to this statement also. As this is the last line of code so the code execution is complete. After completing the code execution,the global execution context is also deleted from the cal stack and the call stack will again be empty.
Getting into the console
Now that we know the theory of execution context, let us try to see this practically. To have a look at this we will just copy this code and paste it in our script file and run it in a web browser. After that now let us go inside the console to see what is happening there.
As we can see from the above picture, the program ran successfully and we got the output 4 and 25 as expected. Now to have a look at the call stack let us try to add debugger at the line1 of our code. We are adding a debugger even before a singel line of code is executed and as we know the global execution context is created and added to call stack even before the program runs so let us try to see it in the console.
In the above picture we can clearly see the global execution context has been created and pushed on to the call stack as shown in the red ellipse above as ({anonymous} index.js). Now let us remove the debugger and try to find our variables and the function with in the global space.
In the above picture we can see that variable 'a' along with its value has been allocated memory.
As seen above the memory has been allocated to the function as well as the variables square1 and square2 . Since we know when a function invocation is encountered in the script, a whole new execution context is created for the function and pushed on top of the stack let us see this in console by adding debugger at the line where function has been invoked.
In the above picture within the red box we can see the execution context of square function has been pushed on top call stack and here we can see the value of 'a' has been passed to function parameter x and the variable result holds the value 4 which is returned by the function. Now as already mentioned above once the program is executed completely, the global execution context is also popped off the call stack here is a picture to see this:
As we have removed the debugger and executed the script file we can see the call stack is empty now hence the global execution context has been removed from it. This was all about the introduction to execution context in JavaScript.