In JavaScript, hoisting allows you to use functions and variables before they are declared. In this post, we will learn what hoisting is and how it works.
Without wasting your time, Let’s begin with hoisting’s explanation.
What is Hoisting in JavaScript?
Hoisting is JavaScript’s default behavior of moving declarations to the top of the function, if defined in a function, or the top of the global context, if outside a function.
What does it mean?
It means if you have declared a variable inside a function then its declaration part will move to the top.
But if your variable is not declared inside any function simply declared it in your program then it globally moves to the top.
Take a look at the code below and guess what happens when it runs:
Example – 1
var name= "Sunil Pradhan"; console.log(name); //Output - Sunil Pradhan
As expected, it prints the value in the name
variable to the console. Then what about the following code?
Example – 2
console.log(name); var name= "Sunil Pradhan"; //Output - undefined
It might surprise you that this code outputs undefined
and doesn’t fail or throw an error – even though name
gets assigned after we console.log
it!
Let us try removing the declaration and see what happens.
console.log(name); //Output - ReferenceError: name is not defined
We got the error this time! What is happening?
In simple terms, we can say that in the second example, the code actually looks like the following to the JavaScript engine,
var name; console.log(name); name = "Sunil Pradhan";
It is putting the declaration of the variable at the top of the code and the name
variable is assigned a value undefined
and because we console it we get the same, this is an example of hoisting.
We can use hoisting with both Variables and Function so let’s take a deeper look at functions and variable hoisting to understand what this means and how it works.
Variable Hoisting in JavaScript
As a reminder, in JavaScript we declare a variable with the var
, let
, and const
.
For example:
var name; let location;
We assign a variable a value using the assignment operator:
// Declaration var name; let location; // Assignment name = 'Sunil'; location = 'Bangalore';
In many cases, we can combine declaration and assignment into one step:
var name = 'Sunil'; let location = 'Bangalore'; const Education = 'B.Tech';
Variable hoisting acts differently depending on how the variable is declared. Let’s begin by understanding the behavior for var
variables.
Variable Hoisting With var
When the interpreter hoists a variable declared with var
, it initializes its value to undefined
. The first line of code below will output undefined
:
console.log(name); // Output - undefined var name = 'Sunil'; console.log(name); // Output - Sunil
As we defined earlier, hoisting comes from the interpreter splitting variable declaration and assignment.
We can achieve this same behavior manually by splitting the declaration and assignment into two steps:
var name; console.log(name); // Output - undefined name = 'Sunil'; console.log(name); // Output - Sunil
Remember that the first console.log(name) outputs undefined
because name
is hoisted and given a default value (not because the variable is never declared).
Using an undeclared variable will throw a ReferenceError instead:
// Uncaught ReferenceError: name is not defined console.log(name);
Using an undeclared variable before its assignment will also throw a ReferenceError because no declaration was hoisted:
// Uncaught ReferenceError: name is not defined console.log(name); // Assigning a variable that's not declared is valid name = 'Sunil';
By now, you may be thinking, “Huh, it’s kind of weird that JavaScript lets us access variables before they are declared.”
This behavior is an unusual part of JavaScript and can lead to errors. Using a variable before its declaration is usually not desirable.
Thankfully the let
and const
variables, introduced in ECMAScript 2015, behave differently.
Variable Hoisting With let and const
Variables declared with let
and const
are hoisted but not initialized with a default value. Accessing a let
or const
variable before it’s declared will result in a ReferenceError:
// Uncaught ReferenceError: // Cannot access 'name' before initialization console.log(name); // Same behavior for variables declared with const let name = 'Sunil';
Notice that the interpreter still hoists name
the error message tells us the variable is initialized somewhere.
The Temporal Dead Zone
The reason that we get a reference error when we try to access a let
or const
variable before its declaration is because of the temporal dead zone (TDZ).
The TDZ starts at the beginning of the variable’s enclosing scope and ends when it is declared.
Accessing the variable in this TDZ throws a ReferenceError.
Here’s an example with an explicit block that shows the start and end of foo
‘s TDZ:
// Start of foo's TDZ let bar = 'bar'; console.log(bar); // "bar" // ReferenceError because we're in the TDZ console.log(foo); let foo = 'foo'; // End of foo's TDZ
Function Hoisting in JavaScript
Function declarations are hoisted, too. Function hoisting allows us to call a function before it is defined.
For example, the following code runs successfully and outputs “Sunil”:
name(); // "Sunil" function name() { console.log('Sunil'); }
Note that only function declarations are hoisted, not function expressions.
This should make sense: as we just learned, variable assignments aren’t hoisted.
If we try to call the variable that the function expression was assigned to, we will get a TypeError or ReferenceError, depending on the variable’s scope:
// Uncaught TypeError: foo is not a function foo(); var foo = function () { } // Uncaught ReferenceError: // Cannot access 'bar' before initialization bar(); let bar = function () { } // Uncaught ReferenceError: // Cannot access 'baz' before initialization baz(); const baz = function () { }
This differs from calling a function that is never declared, which throws a different ReferenceError:
foo(); // Uncaught ReferenceError: foo is not defined
How to Use Hoisting in JavaScript
Variable Hoisting
Because of the confusion that var
hoisting can create, it’s best to avoid using variables before they are declared. If you are writing code in a greenfield project, you should use let
and const
to enforce this.
If you are working in an older codebase or have to use var
for another reason, MDN recommends that you write var
declarations as close to the top of their scope as possible. This will make the scope of your variables more clear.
You can also consider using the no-use-before-define
ESLint rule which will ensure you don’t use a variable before its declaration.
Function Hoisting
Function hoisting is useful because we can hide function implementation farther down in the file and let the reader focus on what the code is doing.
In other words, we can open up a file and see what the code does without first understanding how it’s implemented.
Take the following contrived example:
resetScore(); drawGameBoard(); populateGameBoard(); startGame(); function resetScore() { console.log("Resetting score"); } function drawGameBoard() { console.log("Drawing board"); } function populateGameBoard() { console.log("Populating board"); } function startGame() { console.log("Starting game"); }
We immediately have an idea of what this code does without having to read all the function declarations.
Conclusion
Hoisting in JavaScript is a mechanism in which the variables and function declarations are moved to the top of their scope before the execution of the code.
The function expressions and arrow functions cannot be hoisted. On the other hand, all the variables in JavaScript are hoisted.
Add comment