Online Compiler logoOnline Compiler

JavaScript Tutorial

Var vs Let vs Const — Practical JavaScript Guide

This guide explains `var`, `let`, and `const` with clear rules, runnable examples, and best practices to write safer and more predictable JavaScript.

Why We Need It

Choosing the correct declaration prevents bugs related to scope, hoisting, and accidental reassignment — essential for clean, maintainable code.

Syntax

var name = "Asha";
let count = 0;
const MAX = 10;

Basic Example

1. Block scope vs function scope

if (true) {
  var oldWay = "visible outside block";
  let modernWay = "only inside block";
}

console.log(oldWay); // works
// console.log(modernWay); // ReferenceError

`var` leaks outside block; `let` does not — use block scope for predictable variables.

Real World Example

2. Temporal Dead Zone example

function demo() {
  // console.log(x); // ReferenceError for let/const
  let x = 10;
  console.log(x);
}
demo();

Accessing `let`/`const` before declaration throws due to TDZ — helps avoid accidental early access.

Multiple Use Cases

Quick summary: when to use which

`const` — use it by default for values that should not be reassigned. It provides clearer intent and reduces accidental bugs.

`let` — use when you need to reassign, such as loop counters or stateful variables.

`var` — function-scoped and legacy; avoid in modern code unless maintaining old codebases.

Hoisting and Temporal Dead Zone (TDZ)

Declarations are hoisted differently: `var` is hoisted and initialized with `undefined`, so accessing it before declaration returns `undefined` instead of throwing.

`let` and `const` are hoisted but not initialized. Accessing them before declaration throws a `ReferenceError` — this is called the temporal dead zone.

TDZ catches many accidental early accesses and is a helpful safety feature in modern JavaScript.

Block vs Function Scope

`let` and `const` are block-scoped — they live inside `{}` blocks (for, if, while, etc.). `var` is function-scoped and can escape block boundaries unexpectedly.

Prefer block-scoped declarations to avoid accidental globals and subtle loop bugs.

`const` doesn’t make objects immutable

`const` prevents reassignment of the binding, but object properties can still change.

Use `Object.freeze()` or immutable update patterns when you need deep immutability.

Best practices and migration

Start with `const` as the default and switch to `let` only when reassignment is necessary.

Avoid using `var` in new code; migrate legacy `var` to `let`/`const` as part of refactors.

Keep variable lifetimes small and prefer descriptive names to make intent obvious.

More Examples

3. const with object mutation

const config = { mode: "light" };
config.mode = "dark"; // allowed
console.log(config.mode);

// config = {}; // TypeError if uncommented

`const` locks the binding but not the internal object — mutate properties carefully.

Comparison

Without

// var leaks out of block
if (true) {
  var x = 1;
}
console.log(x); // 1

With

// let stays in block
if (true) {
  let x = 1;
}
// console.log(x); // ReferenceError

Common Mistakes and Fixes

Using `var` in loops leads to closure bugs

Use `let` for loop variables so each iteration gets a fresh binding.

Expecting `const` to be immutable

Use `Object.freeze` or create new objects for immutable updates when needed.

Accessing `let`/`const` before declaration

Declare variables at the top of their block or avoid early access to prevent TDZ errors.

Interview Questions

What is the difference between var and let?

var is function-scoped; let is block-scoped.

What is TDZ?

Temporal Dead Zone prevents accessing let/const before declaration.

Does const make objects immutable?

No. It prevents reassignment, not mutation.

Practice Problem

Practice: Declare a const name and a let counter, then increment the counter.

const name = "Riya";
let count = 0;

// TODO: increment count and log name + count

One Possible Solution

const name = "Riya";
let count = 0;

count += 1;
console.log(name, count);

Frequently Asked Questions

Should I always use `const`?

Yes — prefer `const` by default and use `let` when you need to reassign. This makes code intent clearer and reduces bugs.

Can `const` values change?

The binding cannot be reassigned, but object or array contents can change. Use immutability helpers when you need immutable state.

Why avoid `var` in modern code?

`var` is function-scoped and can lead to unexpected behavior due to hoisting and leakage outside blocks. `let`/`const` are safer and clearer.

Try It Yourself

Run this snippet and see how var leaks outside the block.