# Hoisting in Javascript

One of the most confusing concept in Javascript is **hoisting**. Understanding hoisting is crucial for writing reliable and maintainable code.

## What exactly is hoisting?

When the browser encounters JavaScript code, it creates a special phenomenon known as the [Execution Context](https://shivangyadav.hashnode.dev/decoding-javascript-how-javascript-really-works#heading-the-execution-context-deep-dive). During the creation of the Variable Object, all variables declared with the `var` keyword and function declarations are stored in memory even before the program actually executes, and the value of variables is set to `undefined`. This means that you can use variables and functions in your code before they are formally declared. This process of storing variables and functions before execution is called **hoisting**.

```javascript
console.log(name) //undefined
var name = 'John'
console.log(name) //John
```

In the example above, logging the variable `name` before its initialization doesn't throw an error; instead, it returns `undefined`.

## Types of Hoisting

There are two main types of hoisting in javascript:

1. Variable hoisting
    
2. Function hoisting
    
3. Class hoisting
    

### Variable Hoisting

Variables declared with `var`, `let`, and `const` are all hoisted in JavaScript, but there are differences in how hoisting works with `var` versus `let`/`const`.

* **Hoisting with**`var`: Variables declared with `var` are hoisted, allowing them to be accessed before initialization without throwing an error, albeit returning `undefined`. This behavior can be confusing for developers. To address this, ECMAScript 6 (ES6) introduced new keywords, `let` and `const`, which provide a more predictable behavior.
    
* **Hoisting with**`let` and `const`: let and const are also hoisted, but unlike var they are not initialized and accessing them before initialization results in a ReferenceError: "Cannot access 'variable' before initialization." This behavior highlights the presence of a **Temporal Dead Zone (TDZ)** for `let` and `const` declarations, where the variables exist but cannot be accessed until they are initialized.
    

```javascript
console.log(fruit) //ReferenceError: Cannot access 'fruit' before initialization
let fruit = 'Apple'
```

In the above example we will get ReferenceError: Cannot access 'fruit' before initialization, instead if we have used `var` here instead of `let`, than we get the value of fruit undefined.

**What is Temporal Dead Zone(TDZ)?**

The Temporal Dead Zone refers to the period from the beginning of the scope where the `let` or `const` variable is declared until the point where it is initialized. During this time, accessing the variable will result in a ReferenceError, indicating that the variable cannot be accessed before initialization.

```javascript
let name = 'John'  //TDZ for 'fruit' starts here
console.log(name)  //John
                   
//Temporal dead zone for fruit hence error
console.log(fruit) //ReferenceError: Cannot access 'fruit' before initialization
                   
let fruit = 'Apple' //TDZ for 'fruit' ends here

console.log(fruit) //Apple
```

### Function Hoisting

Function hoisting shares similarities with variable hoisting. Function declarations are hoisted to the top of the scope, allowing them to be invoked before their actual placement in the code.

```javascript
fruits() //Bannana

function fruits() {
 console.log('Bannana')
}
```

In the above example we can see, that we call calling fruits function before its declaration and still printing the result

**Note that only function**`declarations`**are hoisted, not function**`expressions`**.**

If we try to call the variable that the function expression was assigned to, we will get a `TypeError` in case of var and `ReferenceError` in case of let and const.

```javascript
//Case 1
fruits() // TypeError: fruits is not a function
var fruits = function(){
 console.log('Bannana')
}


//Case 2
fruits() // ReferenceError: Cannot access 'fruits' before initialization
let fruits = function(){
 console.log('Bannana')
}
```

**Arrow functions**, introduced in ES6, behaves like let, or const give ReferenceError if called before initilization.

```javascript
fruits() //ReferenceError: Cannot access 'fruits' before initialization

const fruits = () =>{
  console.log("apple")
}
```

### Class hoisting

Just like `let` and `const` variables, classes are hoisted to the top of the scope they are defined in, but inaccessible until they are initialized.

```javascript
let apple = new Fruit("apple") 
//ReferenceError: Cannot access 'fruit' before initialization

class Fruit {
  constructor(name) {
    this.name = name
  }
}
```

Here, we declare a class called `Fruit`. We try to access this class before it was declared and get a reference error: **Cannot access 'Fruit' before initialization**, just like in let and const.

## Summary

In conclusion, variable hoisting is a crucial aspect of JavaScript that impacts how variable declarations are processed during the compilation phase. While `var` exhibits hoisting behavior, it introduces scope-related issues. The introduction of `let` and `const` in ES6 addresses these problems by enforcing block-level scoping and introducing the Temporal Dead Zone.

As a best practice, it is advisable to use `let` and `const` over `var` to write more predictable and maintainable code. Understanding these concepts is essential for every JavaScript developer to produce good and bug-free code.
