Sajiron
Published on Feb 01, 2025var
, let
, and const
in JavaScriptLearn the critical differences between JavaScript’s var
, let
, and const
—scope, hoisting, mutability, and best practices to write clean, bug-free code.
JavaScript developers often encounter var
, let
, and const
when declaring variables. While var
was traditionally used, let
and const
were introduced in ES6 (ECMAScript 2015) to address scoping issues and improve code maintainability. Understanding their differences is essential for writing robust and bug-free JavaScript code.
In this guide, we'll explore the key differences between var
, let
, and const
, and provide best practices to optimize your JavaScript projects.
var: var
keyword has been present since JavaScript was created. However, it has several drawbacks due to its scoping behavior.
let/const: let/const keywords were introduced in ES6 to provide better block scoping and prevent issues caused by var
.
Now, let's discuss the key differences between var
, let
, and const
variables.
1. Variable Scope in JavaScript: var
vs. let
/const
Variable scope defines where a variable can be accessed.
Function Scope with var
var
is function-scoped, meaning var
variables are accessible within the function they are defined in, even if declared inside a block (like an if
statement).
function foo() {
if (true) {
var x = 20;
}
console.log(x); // Output: 20
}
foo();
Block Scope with let
and const
let
and const
are block-scoped, meaning they are confined to the block ({}
) in which they are declared. Typically, this block is the nearest pair of curly braces surrounding the declaration.
function foo() {
if (true) {
let x = 20;
}
console.log(x); // ReferenceError: x is not defined
}
foo();
2. Hoisting: Why var
Behaves Differently Compared to let
and const
Hoisting is the mechanism by which the JavaScript engine allocates memory for variable declarations at the beginning of their scope before executing the code.
var
variables are hoisted to the top of their function or global scope and initialized with undefined
, meaning they can be accessed before declaration but will return undefined
, which can lead to confusing behavior.
console.log(x); // Output: undefined
var x = 10;
let/const
variables are hoisted but not initialized, meaning they exist in a Temporal Dead Zone (TDZ) until their declaration is reached. Accessing them before declaration results in a ReferenceError
.
console.log(x); // ReferenceError
let x = 10;
3. Reassigning and Redeclaring Variables
Keyword | Redeclare? | Reassign? |
| ✅ Yes | ✅ Yes |
| ❌ No | ✅ Yes |
| ❌ No | ❌ No |
Why avoid var? You can redeclare a variable with var
in the same scope without an error.
var count = 5;
var count = 10; // No error
Safer Code with let
and const
let score = 100;
score = 200; // Allowed
const MAX_USERS = 10;
MAX_USERS = 20; // TypeError
4. Mutability: const
Doesn’t Mean Immutable
The const
keyword is used to declare constants, but this does not mean the value is immutable. const
variables behave like let
variables, except they cannot be reassigned after declaration.
const foo = { bar: "a" };
foo.bar = "b"; // Allowed (mutating content)
foo = { bar: "c" }; // TypeError (reassigning reference)
5. Global Scope and the window
Object
In browsers, var
in the global scope attaches to the window
object, risking naming collisions. let
and const
stay private:
var globalVar = "I’m global!";
let privateLet = "I’m not on window";
console.log(window.globalVar); // "I’m global!"
console.log(window.privateLet); // undefined
6. Solving Loop Issues with Block Scoping
A classic var
pitfall in loops with closures:
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i)); // Logs 3, 3, 3
}
Fix with let
:
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j)); // Logs 0, 1, 2
}
In the first loop (var
), there is a single shared i
, so by the time setTimeout
runs, i
is 3
for all iterations. In the second loop (let
), each iteration gets a new i
, preserving its value when setTimeout
executes, resulting in 0, 1, 2
.
Feature |
|
|
|
Scope | Function | Block | Block |
Hoisting | Yes (initialized as | Yes (TDZ applies) | Yes (TDZ applies) |
Redeclaration | Allowed | Not allowed | Not allowed |
Reassignment | Allowed | Allowed | Not allowed |
Must be initialized | No | No | Yes |
Use const
by default for variables that won’t change.
Choose let
when reassignment is needed.
Avoid var
(legacy, error-prone).
This approach minimizes bugs and aligns with modern JavaScript (ES6+).
By understanding the differences between var
, let
, and const
, developers can write cleaner, more predictable, and less error-prone JavaScript code. As a best practice, prefer const
where possible, use let
when necessary, and avoid var
to minimize potential issues.