JavaScript Execution Context & Hoisting
Tags: javascript, frontend, hoisting, executioncontext
Published: 2023-11-22 15:29:50

_Whether you’ve just started learning JavaScript or have some experience, you might have, at least once, searched for how JavaScript works behind the scenes. If so, you might have come across terms like “execution context” and “hoisting,” two topics that are closely related to each other.
Today, you’ll grasp both of them through the most straightforward explanations you’ll ever encounter._

## Introduction
JavaScript is an interpreted, single-threaded language that processes one command at a time. To understand how JavaScript scripts are executed, it’s essential to comprehend the concept of execution context and hoisting.

## Execution Context
The execution context is the environment created when running JavaScript code. There are two types of execution contexts: Global execution context and function execution context. The global execution context represents the global scope, it’s created when JavaScript first runs. The function execution context is created whenever a function is called.

### Execution Context Phases
**1. Creation Phase**

During this phase, the global object is created, the this keyword is generated and linked to the global object, and functions and variables are allocated memory and stored (variables are set to undefined).

**2. Execution Phase**

In this phase, the code is executed line by line, and a new execution context is created whenever a function is called.

Let’s understand it with an example.

```
var x = 3;
function getSquare(n) {
   var square = n ** 2;
   return square;
}
var theSquare = getSquare(x);

console.log(theSquare);
```

**Creation Phase:**

**Line 1:** Allocates memory for the variable `x` and initializes it with the value `undefined`.

**Line 2:** Allocates memory for the function `getSquare()` and stores the entire code within it.

**Line 6:** Allocates memory for the variable `theSqureand` and initializes it with `undefined`.

**Execution Phase:**

**Line 1:** Sets the variable `x `to the value 3.
**Line 2:** Skips the function `getSquare()` as there is nothing to execute.
**Line 6:** Initiates a new execution context (function execution context) as the function is called.
**Creation Phase (function execution context):**

- **Line 2:** Allocates memory for the variable `n `and initializes it with `undefined`.
- **Line 3:** Allocates memory for the variable `square` and initializes 
it with `undefined`.
**Execution Phase (function execution context):**

- **Line 2:** Assigns 3 to the variable `n`.
- **Line 3:** Calculates and assigns the result to the variable `square`.
- **Line 4:** Returns to the global execution context.

Line 6: Assigns the returned value of `square `into the variable `theSqure`.

Now if we console log the output(line8) we will get the following:

![code example](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*0BroGaupL-4ajopsPbLa_g.png)

What if we console log the output before all the code?

```
console.log(theSquare);

var x = 3;
function getSquare(n) {
   var square = n ** 2;
   return square;
}
var theSquare = getSquare(x);
```
The output is undefined
![code example](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*kghMyidkUKhVnI8Pzp2f5Q.png)


You might wonder why the default value for variables is set to undefined. This is a result of the creation phase during the execution context, as discussed earlier. In this phase, variables, including `theSqure`, are allocated memory with an initial value of `undefined`.

Similarly, if you attempt to console log the variable `x` before its initialization, you will also get `undefined`.

```
console.log(x);
var x = 3;
```
![code example](https://miro.medium.com/v2/resize:fit:720/format:webp/1*kghMyidkUKhVnI8Pzp2f5Q.png)

Understanding the execution context and its creation phase simplifies grasping the concept of hoisting.

## Hoisting
Hoisting is frequently described as the interpreter moving variable and function declarations to the top of their scope before code execution. However, now that you understand the creation phase of the execution context, you realize that this explanation is a simplification to help conceptualize the behavior.

**Example1**

**Variable hoisting**

```
console.log(x);
var x = 2;
console.log(x);
```
In this example, even though we console log before the variable `x` is declared and assigned a value, it doesn’t result in an error. Because of hoisting the variable `x` in the first console log outputs `undefined` (Allocates memory for the variable x and initializes it with the value undefined).
![code example](https://miro.medium.com/v2/resize:fit:720/format:webp/1*tLm8xyiOcTR-EssWceYRuA.png)

**Example2**

**Function hoisting**

```
console.log(getCube(2));

function getCube(x) {
   return x ** 3;
}
console.log(getCube(2));
```
Here, the function `getCube()` is called before its declaration in the code. Because of hoisting the entire function gets stored(Allocates memory for the function getCube() and stores the entire code within it.), allowing it to be invoked before its actual placement in the code.
![code example](https://miro.medium.com/v2/resize:fit:720/format:webp/1*bOJfZmO9faZSE9BOiKDo2Q.png)

It’s crucial to distinguish between function declaration and function expression.

```
console.log(getCube(2));

var getCube = (x) => {
   return x ** 3;
};
console.log(getCube(2));
```
In the code, `getCube()` is invoked before it is declared. Due to hoisting, the variable declaration(`var getCube`)is hoisted, but the function definition (`(x) => { return x ** 3; }`)is not. So, when the first console log is encountered, `getCube()` is still `undefined `at that point, and trying to invoke it as a function will result in a `TypeError`.
![code example](https://miro.medium.com/v2/resize:fit:720/format:webp/1*P9gmBC7VR4qn4MOUPLUrag.png)

At this juncture, you may notice that we opted for using `var`for variable declarations instead of `let` or `const`. Why this decision? Simply put, it's to enhance comprehension of the example. While `let` and `const `are block-scoped and also hoisted, variables declared with them are not accessible before initialization—unlike variables declared with `var`, which are hoisted and stored in memory as undefined during the creation phase of the execution context.

The rationale behind the inaccessibility of variables declared with `let` and `const` before initialization lies in what is known as the **Temporal Dead Zone (TDZ)**.

**Temporal dead zone (TDZ)** is the area of a block where a variable is inaccessible until the moment the computer completely initializes it with a value

![A diagram to illustrate the difference between the var and let lifecycles](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w8pxv9hbegh7gvwxs03p.jpg)
A simple diagram to illustrate the difference between the var and let lifecycles

## Conclusion
Whether you’re just starting out with JavaScript or have considerable experience, a thorough understanding of execution context and hoisting is paramount. Delving into the mechanics of how JavaScript functions behind the scenes not only enhances your workflow but also becomes a valuable time-saving skill.