Understanding Javascript Hoisting: A Starter's Guidebook

Introduction

Hoisting is one of the most commonly asked topics when starting with our programming journey in the world of Javascript. Understanding Hoisting is very important before we deep dive into variables, and functions which are the basic yet powerful building blocks in any programming language, especially in Functional programming.

Hoisting is a concept/behavior in JavaScript, where declarations of variables, functions, or classes are moved to the top within their scope when the code is interpreted.

Let's take an example -

function hey() {
  console.log("Hey there!")
}

hey() // Output: Hey there!

We received the desired string in the console, but what if we attempt to call the function before its declaration? Let's witness the magic:

hey() // Output: Hey there!

function hey() {
  console.log("Hey there!")
}

We received the same string, and voilà, there was no error. We can successfully call the function before its declaration.

This is the magic of Hoisting, which we just witnessed. It is a feature not commonly found in other popular programming languages, except for Python.

Hoisting with variables in Javascript

Let's first understand how JavaScript performs this method of moving all the declarations to the top.

Suppose we have the following code -

console.log(a)

var a = "hello";

We're now assuming that code will execute as per the sequence of lines, but while interpreting this is how due to hoisting our above code will be converted -

var a;
console.log(a)
a = "hello";

It's crucial to recognize that only lines with declarations are moved to the top of their scope, while all other lines stay in place.

When the first line executes, a new variable a is declared with the default value of undefined. That's why we see undefined in the second line of the console. The actual value is assigned to the variable when the line containing the original declaration is executed.

var vs let in Hoisting

We already know that there are three ways to declare any variables inside Javascript, but when it comes to hoisting it behaves differently with var and let/const.

Let's check the following code -

// Hoisting with var
console.log(a) // Output: undefined

var a = "hello";
console.log(a); // Output: "hello"

// ------------------------------------------

// Hoisting with let
console.log(b) // ReferenceError: b is not defined

let b = "World";

We can see that with var, the variable is automatically hoisted to the top with a default value of undefined. However, in this case of let, we receive a reference error stating that the variable is not defined.

Now, there is a misconception that hoisting doesn't occur with let/const. But this is not true.

While working with var, variables are hoisted to the top with default value of undefined, but with let/const variables are also hoisted to the top but without any default value and thus it goes to TDZ (Temporal Dead Zone)

Let's visualize:

console.log(a) // Output: undefined
var a;
a = "hello";

console.log(b) // ReferenceError: Cannot access 'b' before initialization
let b;
b = "World";

console.log(c) // ReferenceError: c is not defined

Here, we observed that when we attempted to access b it before its declaration, we received a reference error stating that it could not be accessed before initialization.

However, when we tried to access another variable, c, we encountered an error indicating that the variable is not defined. This is because the interpreter could not find any such variables declared later in the code.

It's worth noting that in some cases, certain interpreters may return the same "variable not defined" error even though the variable was hoisted with let.

Temporal Dead Zone (TDZ)

TDZ of any variable is a zone that starts from the top of the scope where the declaration of any variables happens and ends till the assignment of any value is done for that variable.

This was the reason why we got the reference error while accessing the variable before its assignment because it was present in TDZ.

Hoisting with functions in Javascript

Similar to variables, hoisting also occurs in the function declaration.

foo() // Output: "foo"

function foo() {
  console.log("foo")
}

We can see that with function declarations, we were able to call the function before declaring it.

This time, unlike variables, we got the correct output, as the declaration was hoisted to the top.

foo() // TypeError: foo is not a function

var foo = function () {
  console.log("foo")
}

However, in the case of function expressions, this is not the case. It acts just like a normal variable assignment where a function is assigned to a variable, so we get undefined and, technically, an error when calling functions if called before their declarations.

If we try to check the type of foo -

console.log(typeof foo) // Output: undefined

var foo = function () {
  console.log("foo")
}

We can observe that with function expressions, hoisting follows the variable principle, resulting in undefined being returned because the function was in the Temporal Dead Zone (TDZ).

Hoisting in strict mode in javascript

Till now we have already figured out that despite hoisting being the default behavior of javascript, if not handled properly can result in bugs and faulty code. That's why we have always heard that we should write the code in strict mode.

Let's figure out, how hoisting plays its game in strict mode -

'use strict';

console.log(a) // //ReferenceError: a is not defined

var a = "hello";

When writing the code in strict mode, the Javascript engine prevents the usage of any variable/function before its declaration, and thus technically hoisting is prevented in strict mode.

Summary

This article discusses the concept of Hoisting in JavaScript, which is the default behavior of moving all declarations to the top of their respective scopes during the code interpretation. It begins with the basic understanding of hoisting, followed by its application with variables and functions. It also explains the difference in hoisting behavior with var, let, and const declarations. The concept of Temporal Dead Zone (TDZ) is introduced, explaining why we get a reference error while accessing a variable before its assignment. The article further explores hoisting in function declarations and expressions, and finally, discusses how hoisting is prevented in strict mode.


🌟 Enjoyed the read? I’d love to hear your thoughts! Share your takeaways and let’s start a conversation. Your insights make this journey richer for all of us.

👏 If you found this helpful, a clap or two would mean the world — it helps more folks find these guides!

🔜 What’s next? I’m already brewing the next topic, packed with more insights and a-ha moments. Stay tuned!

🙏 A big thank you for spending time with my words. Your support fuels my passion for sharing knowledge.

👋 Until next time, keep coding and keep exploring!

💬 Let’s stay connected! Follow me on Hashnode for more adventures in coding: Rishav Pandey: Hashnode