JavaScript Interview Prep
JS Engine & Execution

var vs let vs const

The Three Declarations, Fully Decoded

Scenario

for (var i = 0; i < 3; i++) setTimeout(() => console.log(i), 100); prints 3, 3, 3. Change var to let and it prints 0, 1, 2.

If you can explain WHY in one sentence, you understand scope. If you can't, this lesson is for you.

var is function-scoped. let and const are block-scoped. var attaches to window. let and const don't. const prevents reassignment of the binding — NOT mutation of the object it points to. All three are hoisted, but only var is auto-initialized to undefined.


The Full Comparison

This is one of the most asked interview topics. Here's the complete comparison.

Quick Comparison Table

Featurevarletconst
ScopeFunction scopeBlock scopeBlock scope
Hoisted?Yes -> undefinedYes -> TDZYes -> TDZ
Re-declarationAllowedErrorError
Re-assignmentAllowedAllowedError
Global window property?YesNoNo

Scope Difference — The Key Distinction

// var -> function-scoped (ignores blocks like if/for)
function example() {
    if (true) {
        var x = 10;
    }
    console.log(x); // 10 — var leaked out of the if block!
}

// let -> block-scoped (respects all {} blocks)
function example2() {
    if (true) {
        let y = 20;
    }
    console.log(y); // ReferenceError: y is not defined
}

The Classic for Loop Problem

// var: all callbacks share the SAME variable
for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3  (NOT 0, 1, 2!)

// let: each iteration gets its OWN copy
for (let i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2

Why does var print 3, 3, 3? Because var i is function-scoped — there's only ONE i variable. By the time the setTimeout callbacks run, the loop has finished and i is already 3.

Why does let work? Because let i creates a new i for each loop iteration. Each setTimeout callback captures its own copy.

const with Objects & Arrays

const user = { name: "Rakibul" };

// This works — you're modifying the object, not the binding
user.name = "Hassan";
user.age = 25;

// This fails — you're trying to reassign the binding
user = { name: "Someone else" }; // TypeError: Assignment to constant variable

// Same with arrays
const arr = [1, 2, 3];
arr.push(4); // Works
arr[0] = 99; // Works
arr = [5, 6, 7]; // TypeError

const freezes the binding (the reference), not the value. If you want true immutability, use Object.freeze().

var Leaks to window

var globalVar = "I'm on window";
let globalLet = "I'm NOT on window";

console.log(window.globalVar); // "I'm on window"
console.log(window.globalLet); // undefined

This is why var at the global level can accidentally overwrite browser APIs or third-party library globals.


Common Mistakes

  • Treating const as "immutable" — it only prevents re-assignment of the binding; the object it points to can still be mutated. Use Object.freeze() for shallow immutability.
  • Using var inside a loop and expecting each iteration to capture its own value — it won't; all async callbacks share the one function-scoped var.
  • Declaring var at the top of a script and wondering why a library's global got overwritten — var at the top level becomes a property of the window object.

Interview Questions

Q: What's the difference between var, let, and const?

var is function-scoped, hoisted as undefined, can be re-declared and re-assigned, and attaches to window at the top level. let is block-scoped, hoisted into the TDZ, can be re-assigned but not re-declared, and does not attach to window. const is block-scoped, hoisted into the TDZ, and cannot be re-assigned or re-declared — but mutation of the object it references is allowed.

Q: Why does the for loop with var and setTimeout print the same number?

var i is function-scoped — there is only one i for the whole loop. All three callbacks close over the same binding. By the time the setTimeout callbacks fire, the loop has completed and i is 3. let i creates a new per-iteration binding, so each callback captures its own i.

Q: Can you modify a const object's properties?

Yes. const prevents reassignment of the binding (the variable itself), not mutation of the value. Since objects and arrays are reference types, you can modify their properties/elements. Use Object.freeze() for shallow immutability.

Q: What happens if you declare a var variable in the global scope?

It becomes a property of the global object (window in browsers, global in Node). This can accidentally overwrite built-in APIs or third-party globals. let and const declared globally do NOT attach to window, which is safer.

Q: Explain var vs let vs const in one sentence each.

var: function-scoped, hoisted to undefined, attaches to window. let: block-scoped, hoisted into TDZ, re-assignable. const: block-scoped, hoisted into TDZ, binding is locked (but value can still mutate).

On this page