JavaScript Variables Explained: let vs const vs var
Direct Answer & Key Concept
A JavaScript variable is a named storage container for holding data values in memory. Modern JavaScript uses const by default for immutable bindings and let for variables that must be reassigned, completely replacing the obsolete function-scoped var keyword.
// Modern Variable Declaration Best Practices
const API_BASE_URL = "https://api.example.com";
let currentRetryCount = 0;
function incrementRetry() {
currentRetryCount += 1;
const isMaxReached = currentRetryCount >= 3;
return { currentRetryCount, isMaxReached };
}
console.log(incrementRetry()); // { currentRetryCount: 1, isMaxReached: false }
console.log(incrementRetry()); // { currentRetryCount: 2, isMaxReached: false }What is a variable in JavaScript?
A JavaScript variable is a symbolic container used to store, retrieve, and manipulate data values in computer memory throughout an application's lifecycle. Since the ECMAScript 2015 (ES6) specification, JavaScript provides three declaration keywords—const, let, and the legacy var—each governed by distinct scoping rules, temporal dead zones, and mutability guarantees.
Understanding how JavaScript variables work is essential because incorrect scoping or accidental global assignments are among the most common sources of bugs in web development.
How do let, const, and var differ?
The table below contrasts the fundamental behavior of JavaScript's variable declaration keywords across scope boundaries, hoisting lifecycle, and reassignment constraints:
| Keyword | Scope Level | Reassignable? | Re-declarable? | Hoisting Behavior | Temporal Dead Zone (TDZ)? |
| :--- | :--- | :--- | :--- | :--- | :--- |
| const | Block ({}) | No (constant reference) | No | Hoisted (uninitialized) | Yes |
| let | Block ({}) | Yes | No | Hoisted (uninitialized) | Yes |
| var | Function / Global | Yes | Yes | Hoisted (initialized to undefined) | No |
When should you use const?
Use const by default for every variable declaration in your codebase. Declaring with const prevents accidental reassignment and immediately signals to teammates and compilers that the identifier binding will not change. Crucially, while a const reference cannot be reassigned, the internal properties of objects or elements of arrays declared with const can still be mutated unless protected with Object.freeze().
When should you use let?
Use let exclusively when you know a variable must be reassigned later in its lexical lifecycle. Typical use cases include loop counters (for (let i = 0; ...)), mathematical accumulators, and toggles within conditional branching.
Why should you avoid var in modern JavaScript?
The legacy var keyword is function-scoped rather than block-scoped and leaks outside of if statements and for loops. Furthermore, var allows dangerous re-declarations within the same scope and silently initializes hoisted variables to undefined, which can mask syntax errors and introduce elusive race conditions.
What is Block Scope vs Function Scope?
In JavaScript, block scope means a variable declared inside a pair of curly braces { ... } (such as inside an if statement, while loop, or switch block) exists only inside that specific block and cannot be accessed from outside.
if (true) {
let blockScoped = "I live only inside this if block";
var functionScoped = "I leak outside this if block!";
}
console.log(functionScoped); // Prints: "I leak outside this if block!"
// console.log(blockScoped); // ReferenceError: blockScoped is not defined
Both let and const strictly enforce block scoping, guaranteeing that temporary loop counters and condition variables do not leak into surrounding application logic.
What is the Temporal Dead Zone (TDZ)?
The Temporal Dead Zone (TDZ) is the period between entering a lexical scope and the actual line of code where a let or const variable is declared. Accessing the variable during this window throws an immediate ReferenceError.
// Attempting to access before declaration
// console.log(userScore); // ReferenceError: Cannot access 'userScore' before initialization
let userScore = 95; // Scope initialized here
console.log(userScore); // 95
Unlike var, which initializes to undefined during hoisting, the TDZ actively prevents developers from reading uninitialized state before its intended declaration point.
Common Mistakes with JavaScript Variables
- Reassigning a const identifier: Attempting
const port = 3000; port = 8080;causes an unrecoverableTypeError: Assignment to constant variable. - Confusing const reference with deep immutability: Declaring
const config = { theme: 'dark' };allowsconfig.theme = 'light'. UseObject.freeze()if true property immutability is required. - Accidental global variable creation: Forgetting a declaration keyword in non-strict mode assigns the variable to the global window or globalThis object, polluting global state.
- Variable shadowing: Declaring a variable with the same identifier in an inner scope obscures the outer variable, creating confusion during maintenance.