What are the potential issues caused by hoisting?
TL;DR
JavaScript creates bindings before executing a scope's statements, but initializes different declaration forms differently. An early read of var produces undefined; an early read of let, const, or class throws because the binding is in the temporal dead zone; and function declarations are already callable. These differences—not literal source-code movement—can produce confusing bugs.
console.log(a); // undefinedvar a = 5;console.log(b); // ReferenceError: Cannot access 'b' before initializationlet b = 10;
Potential issues caused by hoisting
Variables being undefined
When using var, the binding is created and initialized to undefined during scope setup. It retains that value until the assignment executes.
console.log(a); // undefinedvar a = 5;
Temporal dead zone with let and const
Variables declared with let and const are also hoisted, but they are not initialized. Accessing them before their declaration results in a ReferenceError due to the temporal dead zone.
console.log(b); // ReferenceError: Cannot access 'b' before initializationlet b = 10;
Function declarations vs. function expressions
Function declarations are initialized with their function during scope setup. A function expression is created only when its expression runs, so a var that will later receive a function still contains undefined beforehand and calling it throws TypeError.
foo(); // Works finefunction foo() {console.log('Hello');}bar(); // TypeError: bar is not a functionvar bar = function () {console.log('Hello');};
Redeclaration issues with var
Using var can lead to unintentional redeclarations, which can cause bugs that are hard to track down.
var x = 1;if (true) {var x = 2; // Same variable as above}console.log(x); // 2