javascript var vs let vs const
The javascript var vs let vs const debate is a cornerstone for anyone learning modern JavaScript. Now, understanding when to use var, let, and const helps you write clearer code, avoid subtle bugs, and improve performance. This article breaks down each keyword, explains their behavior, and offers practical guidance for everyday development Not complicated — just consistent. That alone is useful..
Understanding var
var is the oldest way to declare variables in JavaScript, dating back to the language’s inception. Its most notable characteristic is function‑scoped nature: a variable declared with var is accessible throughout the entire function (or the global scope if outside a function) Most people skip this — try not to..
Key traits of var
- Function scope – the variable lives until the end of the surrounding function, not the block it appears in.
- Hoisting – declarations are hoisted to the top of their scope, meaning you can use the variable name before the actual declaration, though its value is
undefined. - Re‑declaration allowed – you can redeclare the same variable anywhere in the same scope, which can lead to accidental overwrites.
function example() {
console.log(x); // undefined, because of hoisting
var x = 10;
var x = 20; // re‑declaration is permitted
}
Because of these traits, var is generally discouraged in favor of let and const, especially in larger codebases where block‑level scoping is preferred.
Understanding let
let was introduced in ES6 (2015) to address many of var’s shortcomings. It provides block‑scoped declaration, meaning the variable is limited to the block in which it is declared (e.g., inside a {} block of a for loop, an if statement, or a {} wrapper).
Key traits of let
- Block scope – the variable cannot be accessed outside the block that encloses it.
- Temporal Dead Zone (TDZ) – the variable is not accessible until the line where it is declared, even though the declaration is hoisted. Attempting to read it before the declaration throws a
ReferenceError. - Single declaration – you cannot redeclare the same variable within the same block.
if (true) {
let y = 5;
console.log(y); // 5
}
console.log(y); // ReferenceError: y is not defined
let shines when you need a variable that should not leak outside its intended block, such as loop counters or temporary values inside conditionals.
Understanding const
const also belongs to the ES6 family and builds on let’s block‑scoped behavior. The primary difference is that the binding is constant: once you assign a value to a const variable, you cannot reassign it.
Key traits of const
- Block scope – same as let, the variable is limited to its surrounding block.
- Immutable binding – you cannot reassign the variable name to a new value.
- Deep immutability – for primitive values (numbers, strings, booleans) this means true constancy; for objects and arrays, the reference is constant, but the contents can still be mutated.
const z = { name: "Alice" };
z.name = "Bob"; // allowed, because the object reference itself didn't change
// z = {}; // TypeError: Assignment to constant variable.
If you need a truly immutable value, consider using primitive types or freezing objects/arrays.
Key differences at a glance
| Feature | var | let | const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisting behavior | Hoisted, value undefined |
Hoisted, TDZ (uninitialized) | Hoisted, TDZ (uninitialized) |
| Re‑declaration | Allowed | Not allowed | Not allowed |
| Re‑assignment | Allowed | Allowed | Not allowed |
| Typical use case | Legacy code | Variable that changes | Value that never changes |
Understanding these distinctions helps you choose the right keyword for the job, reducing bugs caused by unintended scope changes or accidental reassignment Worth keeping that in mind..
Practical examples
Using var in a loop
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Logs 3 three times because i is function‑scoped
Using let in a loop
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 100);
}
// Logs 0, 1, 2 – each iteration gets its own block‑scoped variable
Using const for a configuration object
const config = {
apiKey: "12345",
baseUrl: "https://api.example.com"
};
config.baseUrl = "https://new.Also, api. example.com"; // OK, object is mutable
// config = {}; // TypeError: Assignment to constant variable.
## Common pitfalls and best practices
- **Avoid var in new code** – it can cause unexpected scope leaks and hoisting issues.
- **Prefer let for variables that change** – it limits the variable’s visibility to the block where it’s needed, preventing accidental modifications.
- **Use const by default** – if you’re not planning to reassign the variable, const makes your intent explicit and helps the engine optimize memory usage.
- **Beware of mutable objects with const** – the reference is constant, but nested objects or arrays can still be altered. Freeze them if true immutability is required: `Object.freeze(obj)`.
- **apply IDE warnings** – modern editors flag attempts to reassign const variables, catching mistakes early.
## Frequently asked questions
**Q1: Can I redeclare a `let` variable in a different block?**
A: Yes. Each block creates its own scope, so redeclaring `let x` in separate blocks does not conflict.
**Q2: Does `const` make an object immutable?**
A: No. It prevents reassignment of the variable, but the object’s properties can still be changed unless you take extra steps to freeze it.
**Q3: Is `var` still useful in any scenario?**
A: It can be useful when you deliberately need function‑scoped variables, especially in legacy codebases, but newer projects should avoid it.
**Q4: What happens if I try to access a `let` variable before its declaration?**
A: A `ReferenceError` is thrown because the variable resides in the Temporal Dead Zone until the declaration line executes.
**Q5: Can I use `const` with primitive values inside a loop?**
A: Yes, but the value itself will change on each iteration; the binding remains constant.
## Conclusion
The **javascript var vs let vs const** discussion is more than a syntax choice; it reflects how JavaScript’s scoping model has evolved to give developers finer control over variable lifetimes and visibility. By applying these concepts thoughtfully, you write safer, more maintainable code that aligns with modern JavaScript best practices. **Var** remains functional but limited, **let** offers block‑level flexibility for values that change, and **const** signals intent immutability while still allowing mutable structures when needed. Embrace **let** and **const** as your default tools, reserve **var** for legacy contexts, and watch your code become clearer and bug‑free.