Demystifying Functions in JS

Demystifying Functions in JS

ยท

5 min read

Functions are small programs in themselves that help to increase the readability and reusability of our code. Functions have their own execution context where the entire code present in the function gets executed. Similar to the global execution context, the function execution context also consists of two phases - The creation Phase and the Execution Phase. Creation Phase is the phase in which arguments object will be created, this variable is declared and referenced to the Global Object and finally creating and storing all the other variables in the function to the memory heap. The execution phase is the phase in which all the variables will be assigned to their actual values and functions will be executed. Now let's try to dig deep with the help of an example.

function printIcecreamFlavour(flavour) {
  console.log(`Icecream ${flavour}:`);
}
function calculateCost(price, numberOfIcecreams) {
  return price * numberOfIcecreams;
}

printIcecreamFlavour("Vanilla");
var price = calculateCost(10, 5);
console.log(price);

Screenshot 2021-09-20 at 1.00.22 AM.png

Screenshot 2021-09-20 at 1.07.24 AM.png

When the Javascript engine runs the above code, at first it will create a global execution context. Global execution context would contain two parts - one part would contain the entire code and the other part would be the place where the variables and functions are stored. After the execution context is created, the creation phase get's initiated. During this phase, functions printIcecreamFlavour and calculateCost get stored in the memory with their respective function codes as their values while the variable price will be stored in the memory with undefined as its default value.

Screenshot 2021-09-20 at 1.11.07 AM.png

Screenshot 2021-09-20 at 1.13.23 AM.png

Soon after the creation phase, the execution phase get's initiated. During this phase, the JS engine executes the code line by line. As soon as it encounters line 8, it checks whether this identifier is present in the local memory, if it's present, it will get the value which in this case would be the entire function code and executes it. As soon as the JS engine executes the function, it will push the function to the call stack and creates a new execution context for the function printIcecreamFlavour. This execution context will be containing two parts - one part where the function code will reside and the other part where all the variables and functions will be stored. Soon after the execution context is created, the creation phase gets initiated. In this phase, the arguments object will be created, containing the values passed to the function. Since there are no more declaration statements, it moves to the execution phase. In this phase, as it encounters the console.log(Icecream ${flavour}), it will first check whether there is an identifier called console present in the local memory or not. As it is not present in the local memory, it will move to the global execution context and check its memory. This works based on the scope rules defined in Javascript. If you are struggling to understand how scope works in JS, you can check this.

Screenshot 2021-09-20 at 1.16.43 AM.png

Screenshot 2021-09-20 at 1.17.36 AM.png

As it finds the object console, it will search for the method log defined in the object and execute the log function in a similar fashion where the log function is pushed to the call stack and a new execution context for the log function is created. On executing the function, Icecream Vanilla will be printed on the console. Soon after the execution, the execution context of log will be deleted and popped from the call stack. Since there are no more statements left to be executed in printIcecreamFlavour function, the execution context of printIcecreamFlavour will be deleted and popped from the call stack. Now the current execution context will be the global execution context and the JS engine will continue executing the next statement. When it encounters the statement var price = calculateCost(10, 5), it will first check for the identifier price. As it's present, it will assign the value of calculateCost. As calculateCost is a function, the price will be assigned only a value after the completion of calculateCost function. When calculateCost is executed, the function will be pushed to the call stack and a new execution context will be created for calculateCost. Similar to the previous function at first the creation phase gets initiated. During this phase, the arguments object will be created which would contain the values of price and numberOfIcecreams that are passed as arguments into the function. Since there are no more declaration statements, it starts the execution phase. During this phase, the JS engine starts executing the code line by line. As soon as it encounters the statement return price * numberOfIcecreams;, it will get the value of price and numbeOfIcecreams from the memory, evaluate the expression price * numberOfIcecreams and return that value which is then assigned to the variable price.

As there are no more statements to be executed in the function, the function execution context will be destroyed and calculateCost function will be popped from the call stack where the global execution context would be the current execution context. Now JS engine proceeds to the next statement which would be the console.log(price). On executing this, it will fetch the value of price from the memory and print it to the console. As there are no more statements to be executed, the JS engine will destroy the global execution context and pop it from the call stack.

So what about function expression ???. Well, I will be soon covering this in detail in my next blog, hold tight. Till then, if you feel this article was useful, please do show your love and share this with 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 ๐Ÿฅ‚