Hoisting in Javascript

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. 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.

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 withvar: 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 withlet 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.

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.

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.

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 functiondeclarationsare hoisted, not functionexpressions.

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.

//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.

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.

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.