Hoisting Behavior with var
When using var, declarations are hoisted to the top of their scope, but initializations remain in place. This often leads to unexpected undefined values in function scopes.
var globalId = 100;
processId();
console.log(globalId); // 100
function processId() {
console.log(globalId); // undefined (local declaration hoisted)
var globalId = 5;
console.log(globalId); // 5
}Block Scope Constraints
Before ES6, JavaScript only supported global and function scopes. The var keyword ignores block boundaries like curly braces, whereas let restricts the variable strictly to the block where it is declared.
{
var status = "active";
}
console.log(status); // "active"
{
let level = "admin";
}
console.log(level); // ReferenceError: level is not definedIteration and Closure Boundaries
A classic scenario involves asynchronous operations inside loops. With var, a single binding is shared across all iterations, causing callbacks to reference the final value. Using let creates a fresh binding for each iteration, preserving the correct value inside closures.
for (var count = 0; count < 5; count++) {
setTimeout(() => {
console.log(count); // Outputs 5 five times
}, 0);
}
for (let step = 0; step < 5; step++) {
setTimeout(() => {
console.log(step); // Outputs 0, 1, 2, 3, 4 sequentially
}, 0);
}Temporal Dead Zone
Variables declared with let are not accessible before their declaration line is executed, even within the same block. Accessing them earlier triggers a ReferenceError. This region before the declaration is known as the Temporal Dead Zone (TDZ).
console.log(appName); // ReferenceError: Cannot access 'appName' before initialization
let appName = "SystemDashboard";Redeclaration Restrictions
Unlike var, which permits multiple declarations of the same variable in an identical scope without throwing errors, let strictly forbids this practice.
let config = true;
let config = false; // SyntaxError: Identifier 'config' has already been declared