Hoisting has been one of the confusing and misunderstood concepts in JavaScript to beginners and many advanced developers. In this article, we will dive into Hoisting, how it works and the pros and cons attached to it.
What is Hoisting
Hoisting is the moving up of all variable declarations, functions, and classes to the top of their scope by the JavaScript Interpreter before being executed. Some variables and functions can be accessed before they’re declared, This makes the code cleaner and readable by separating the initialization from where they are called.
Variable in JavaScript
Before diving into Hoisting, let's first understand how variables are declared and assigned in JavaScript.
Variables in JavaScript are declared using var
, let
and const
keywords (const
and let
are ES6 variables introduced in 2015). The const
variable can not be reassigned in the code unlike the var
and let
variable.
Examples of variable declaration:
// Variable Declaration
var myName;
let age;
Variables are assigned using the assignment operator( = ):
var myName;
let age;
// variable declaration
myName = "sam";
age = 24;
// variable assignment
The image above shows that the const
keyword cant be declared or re-assigned.
But in most cases, variables are declared and assigned in a single line (e.g the const
variable).
var myName = "Sam";
let age = 24;
const hobby = "Coding";
Var Variable Hoisting
console.log(myName);
var myName;
myName = "Sam";
// Log
undefined
From the code above, variables declared with var
are actually hoisted but their initial value is set to undefined.
Let and Const Hoisting
// variables declared with let and const
console.log(age);
console.log(hobby);
let age = 24;
const hobby = "coding";
// Log
Uncaught ReferenceError: Cannot access 'age' before initialization
Uncaught ReferenceError: Cannot access 'hobby' before initialization
From the code above, variables declared with let
and const
throws an error because they are in the TDZ(Temporal Dead Zone) and can’t be used or accessed before initialization, thus hoisted technically.
What is TDZ?
The TDZ is known as a Temporary Dead Zone. This is the space between the beginning of the current scope to the line or point where the variable declared with let
or const
is initialized, be it in a global or function scope.
TDZ of let
// TDZ of let
//------------------------>the TDZ starts here
console.log(age);//------->TDZ
//------------------------>TDZ
//------------------------>TDZ
let age = 24;//----------------->TDZ ends here
TDZ of const
//TDZ of const
//--------------------------->The TDZ starts here
console.log(hobby);//-------->TDZ
//-------------------------->TDZ
//--------------------------->TDZ
const hobby = "coding"; //--------->TDZ ends here
Why TDZ?
The TDZ was introduced in ES6 (ECMAScript 6) alongside let
and const
and one of the primary reasons for its introduction was to Avoid, Prevent and Catch errors and bugs. TDZ was introduced before using the var
keyword to set initial values created bugs in large javaScript codebases. TDZs also prevent the const
keyword from setting an initial value of undefined and reassigned after declaring it in another variable in a codebase.
How Functions are hoisted
Functions are hoisted slightly differently from variables in JavaScript. There are three (3) ways of creating functions in JavaScript namely Function declaration, Function Expressions, and Arrow Functions.
Hoisting with Function Declaration
// Hoisting in Function Declarations
greetDeclaration();
function greetDeclaration() {
console.log("Hello world from Hoisting in Function Declaration");
}
// Log
Hello world from Hoisting in Function Declaration
In the above code, the function was executed smoothly without errors. This is because Function declarations are hoisted, and their initial value is set to the Actual Function.
Function Expression
Function Expressions are normal functions that are stored or assigned to a variable.
// Hoisting in Function Expression
greetExpression();
const greetExpression = function () {
console.log("Hello world form Hoisting in Function Expression");
};
// Log
Uncaught ReferenceError: Cannot access 'greetExpression' before initialization
The code above throws a Reference uninitialized error because the JavaScript Interpreter treats the functions as a variable. Since it's declared with a const
, it is expected to get the same result as a variable declared with const
. Thus hoisted technically but cant be used or accessed before initialization.
Arrow Functions
Arrow functions, also known as the Anonymous function, are normal variables that store a function.
// Hoisting in Function Expression
greetArrow();
const greetArrow = () => console.log("Hello world form Hoisting in Function Arrow");
// Log
Uncaught ReferenceError: Cannot access 'greetArrow' before initialization
The code above throws a Reference uninitialized error because it’s an ES6 function, and since ES6 was released to help prevent bugs, this makes arrow functions be hoisted uninitialized instead of undefined like var.
Hoisting of Function Expression declared with var
// Function Expression declared with var
greetExpression();
var greetExpression = function () {
console.log("Hello world form Hoisting in Function Expression");
};
// Logs
Uncaught TypeError: greetExpression is not a function
Functions declared with var
throws a TypeError because the JavaScript Interpreter sets the function to undefined, thus making the whole function return undefined.
Hoisting of Arrow Function declared with var
//Arrow Function declared with var
greetArrow();
var greetArrow = () =>
console.log("Hello world form Hoisting in Function Expression");
// Log
Uncaught TypeError: greetArrow is not a function
Like the Function declaration, Arrow Functions declared with var
throws a TypeError because the JavaScript Interpreter sets the function to undefined, thus making the whole function return undefined.
Hoisting in ES6 Classes
Classes in JavaScript are a blueprint, framework, or template for creating objects in ES6 JavaScript. They are also known as Syntactical sugar for creating objects. Classes can be created using declarations or expressions, just like functions.
Hoisting in Class Declarations
var car = new Car();
car.color = "Jane";
console.log(car);
class Car {
constructor(color) {
this.color = color;
}
}
// Log
// Uncaught ReferenceError: Cannot access 'Car' before initialization
Hoisting in class declarations throws a ReferenceError which is similar hoisting a variable declared with let
or const
before it is initialized in our code.
Hoisting in Class Expressions
//Class Experssions
const instance = new Food();
console.log(instance.order());
const Food = class {
constructor() {}
order() {
return 'What dish do you want?!';
}
};
//Log
Uncaught ReferenceError: Cannot access 'Food' before initialization
Class Expressions also experience the const
treatment like in functions. They can’t be hoisted to the actual object or to undefined. Thus they are uninitialized.
Hoisting in Class Expressions declared with var
//Class Experssions declared with var
const instance = new Food();
console.log(instance.order());
var Food = class { //declared with var
constructor() {}
order() {
return 'What dish do you want?!';
}
};
//Log
Uncaught TypeError: Food is not a constructor
Hoisting in Classes declared with var
returns a TypeError just like in functions. This is caused by the JavaScript Interpreter setting the class to undefined.
Hoisting Table
This is a comprehensive list hoisting in all variable declarations, expressions, functions, and classes.
Conclusion
In this article, we looked at Hoisting, what it is, and the way it works with variables, functions, and classes. How using var
can be the source of bugs in large JavaScript codebases and how ES6 modules like let
and const
might be better alternatives and can serve as a workaround to var
.
I hope that you finally understand what Hoisting is, it’s use cases and why it is important in JavaScript.