JavaScript Runtime Environment & Hoisting

Photo by Denys Nevozhai on Unsplash

“You have to know where you were going in order to get there.” ― Suzanne Weyn

What is a Runtime Environment?

In order to run a computer program written in a programming language, there should be many processes and steps involved, the program should communicate with the hardware, OS, and also complex tasks such as managing the heap and stacks, memory management, garbage collections, and many more.

This process does not handle by the programmer and this is a very dynamic process that differs from program to program. A runtime environment provides the ability to manage the above process and run the programs on different platforms.

JavaScript Runtime Environment

JavaScript is a high-level language because before executing a program written in JavaScript should be interpreted or compiled (Based on the engine) into the machine code. and the execution of the program handled by a specific program known as “JavaScript Runtime Engine”.

Every browser comes with a specific JS Runtime engine and does not need to install separately, those are

V8 Engine - Used in Google Chrome web browser and NodeJS. Developed using C++.

SpiderMonkey - Used in Firefox and developed using C++.

Nitro - Used in Safari web browser.

Chakra - Used in Microsoft Edge web browser.

These implementations of each JavaScript engine are completely different from one another but in order to maintain a standard, all of these engines are based on ECMAScript specification and ensure that the given same JS program runs on each engine without modifications.

How JavaScript Program Works

Before finding out what’s inside the JavaScript engine, Let’s find out how the JavaScript program executes.

At first when a JS program starts to run it runs in an execution context. There are two execution contexts

  1. Global Execution Context
  2. Functional Execution Context

Each of these execution contexts contains two main phases of program execution, those are.

1. Creational Phase

  • Creating an activation object by scanning the JS code and list down variables and declarations.
  • Creating a Scope Chain - This will list down all the variables in the current function.
  • Creating global variable “this”. (“window” for client-side JS)

2. Execution Phase

  • Code execution starts at this phase, Values will be assigned to variables listed down in the creational phase. If it’s a function it will execute in a separate functional execution context.

Let’s now look at how this works inside.

In the above program, we define two variables at first and a function that concatenates two strings and another variable that holds the results of the returned value of the function.

Let’s now see how this program works using a simple diagram.

As you can see at the creational phase the values will be undefined but when it comes to the execution phase the values for each variable will be assigned and for each function, it creates a separate “Functional Execution Context”. And after execution of the function, the value will be returned and the “functional execution context” will be destroyed.

Now let’s assume there are recursion functions or there are functions that might call other functions inside it. At that time JS engine should keep track of the order of the function execution this will be handled by the “Call Stack”.

Here’s a simple example of how it works

For the above function, the call stack will be like this.

However, it is important to be aware that when calling functions inside another function and recursive functions it is important to follow best practices of coding otherwise the functions keep creating functional execution context inside its parent’s functional execution context and this will leads to a stack overflow error.

Hoisting

In the earlier application, we saw that in the creation phase the variables in the global execution context will be defined as “undefined”.

The process happens as follows when the application loads to the JS engine it first lists out all of the variables in the global execution context. and move all into the top, but it does not assign the real values, this concept is called Hoisting.

But this behaves in different ways in different cases.

Variable Hoisting

There are certain ways of defining variables those are var, let, and const. when it comes to the hoisting. Now let’s see an example using a simple program.

If we inspect those variables individually (in developer console or running on NodeJS) the result will be as follows.

Line 1 - undefined
Line 2- ReferenceError: Cannot access ‘b’ before initialization
Line 3- ReferenceError: Cannot access ‘c’ before initialization

As we can see the program has identified that there is a variable named “b” and “c” which means hoisting works for all three ways of variable declaration types. But when it comes to the const and let it throws an exception.

The reason for that is in the JS engine variables defined as const and let are stored in the Script Scope and meanwhile, the variables defined as var stored in the Global Scope.

Variable Scopes using Dev Console

and JS engine do not provide access to the script scope. in order to access the variables defined using let and const should be initialized with a value at first.

Function Hoisting

Hoisting also works with the functions, There are two ways of defining functions for both ways hoisting acts differently. Let’s look at an example of how hoisting behaves with functions.

In the above program, we have defined functions, one is a function (add), an arrow function (multiply) and the other one is a function reference (divide). The output will as below.

answerOfAdd : 30
answerOfMultiply: throws an error “ReferenceError: multiply is not defined”
answerOfDivide : throws an error “TypeError: divide is not a function”

As per the results, we can see when it comes to the function it only supports the functions not for arrow functions neither function references, but hoisting works for both of those.

This article was inspired by the following videos which explaining and visualizing how JS runtime engine and hosting works with a practical approach.

JavaScript Execution Context.

JavaScript Execution Context Visualization.

How Hoisting works.

Software Engineer