JavaScript Variables: The Complete Guide to var, let, and const

JavaScript Variables: The Complete Guide to var, let, and const

Quick Summary: JavaScript offers three ways to declare variables: var (the old way), let and const (modern ES6+). Understanding their differences is crucial for writing bug-free, maintainable code.

📊 Quick Comparison Table

Featurevarletconst
ScopeFunctionBlockBlock
Re-declare✅ Allowed❌ Not allowed❌ Not allowed
Re-assign✅ Allowed✅ Allowed❌ Not allowed
HoistingYes (undefined)Yes (TDZ)Yes (TDZ)
When to UseAvoid

1. var – The Legacy Way

Function-scoped, hoisted, and full of quirks

🔍 Key Characteristics:

  • Function-scoped (or global if declared outside functions)
  • Hoisted and initialized with undefined
  • Can be re-declared in the same scope
  • Becomes a property of the global object (window in browsers)

💻 Code Examples:

// Function scope example
function exampleVar() {
  if (true) {
    var x = 10; // Function-scoped, not block-scoped
  }
  console.log(x); // 10 - accessible outside the if block
}

// Hoisting example
console.log(y); // undefined (not an error!)
var y = 5;

// Re-declaration allowed
var z = 1;
var z = 2; // No error

2. let – The Modern Standard

Block-scoped, predictable, and recommended

🔍 Key Characteristics:

  • Block-scoped (confined to {})
  • Hoisted but in Temporal Dead Zone (TDZ)
  • Cannot be re-declared in the same scope
  • Can be re-assigned (mutable)

💻 Code Examples:

// Block scope example
function exampleLet() {
  if (true) {
    let x = 10; // Block-scoped
    console.log(x); // 10
  }
  // console.log(x); // ReferenceError: x is not defined
}

// Temporal Dead Zone (TDZ)
// console.log(a); // ReferenceError: Cannot access before initialization
let a = 5;

// No re-declaration
let b = 1;
// let b = 2; // SyntaxError: Identifier 'b' already declared

// Loop example (solves closure problem)
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // Logs: 0, 1, 2
}

3. const – The Constant Champion

Block-scoped, immutable reference, best practice

🔍 Key Characteristics:

  • Block-scoped like let
  • Cannot be re-assigned after declaration
  • Must be initialized during declaration
  • Object/array contents can be modified (mutation allowed)

💻 Code Examples:

// Basic const usage
const PI = 3.14159;
// PI = 3.14; // TypeError: Assignment to constant variable

// Objects can be modified
const user = { name: "John", age: 30 };
user.age = 31; // ✅ Allowed (mutation)
user.city = "NYC"; // ✅ Allowed
// user = {}; // ❌ Not allowed (reassignment)

// Arrays can be modified
const colors = ["red", "green"];
colors.push("blue"); // ✅ Allowed
colors[0] = "yellow"; // ✅ Allowed

// Must be initialized
// const VALUE; // SyntaxError: Missing initializer
const VALUE = 100;

⏳ Understanding Temporal Dead Zone (TDZ)

The TDZ is the period between entering a scope and the actual declaration where variables exist but can’t be accessed.

// TDZ starts at beginning of scope
console.log(value); // ❌ ReferenceError: TDZ!

let value = "Hello"; // TDZ ends here

console.log(value); // ✅ "Hello"

Why TDZ Matters:

  • Catches bugs early (use before declaration)
  • Makes code more predictable
  • Applies to both let and const
  • Doesn’t apply to var (initialized with undefined)

🎯 Best Practices & Recommendations

Use const by Default

Start with const for all variable declarations. Only use let when you know the value needs to change.

// ✅ Good
const API_URL = "https://api.example.com";
const config = { timeout: 5000 };
const users = ["Alice", "Bob"];
🔄

Use let for Mutable Values

When a variable’s value needs to change (counters, flags, accumulators), use let.

// When reassignment is needed
let counter = 0;
counter = 1; // ✅

let isLoading = false;
isLoading = true; // ✅
🚫

Avoid var in New Code

var has quirks that lead to bugs. Use let and const in all new JavaScript code.

// ❌ Avoid
var oldVariable = "outdated";

// ✅ Use instead
const newVariable = "modern";
let mutableVariable = "changeable";

🌐 Real-World Example: Loop Behavior

This classic example shows why let is superior to var in loops:

❌ Problem with var

for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // Outputs: 3, 3, 3
  }, 100);
}

All callbacks reference the same i variable.

✅ Solution with let

for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // Outputs: 0, 1, 2
  }, 100);
}

Each iteration gets its own i binding.

💼 Common Interview Questions

Q1: Can you modify a const object?

Yes! You can modify the contents of a const object or array, but you cannot reassign the variable itself.

const obj = { a: 1 };
obj.a = 2; // ✅ Allowed
obj.b = 3; // ✅ Allowed
// obj = {}; // ❌ Not allowed

Q2: What’s the output of this code?

console.log(x);
let x = 5;

ReferenceError: Cannot access ‘x’ before initialization due to Temporal Dead Zone.

Q3: When should you use let vs const?

Default to const for all declarations. Only use let when you specifically need reassignment capability.

📝 Summary & Final Thoughts

  1. const is your default choice – Use it for 90% of variable declarations
  2. let is for mutable values – When you need to reassign a variable
  3. Avoid var in new code – Its quirks lead to bugs and confusion
  4. Understand block vs function scope – Critical for predictable code
  5. Remember TDZlet and const are hoisted but not initialized

💡 Pro Tip:

Configure your linter (ESLint) to enforce using const by default and flag any usage of var as an error.

Leave a Reply

Your email address will not be published. Required fields are marked *