Skip to content
Innovialabs

var vs let vs const in JavaScript: Scope and Hoisting

Basics4 min

In short

Use const by default and let when the variable must be reassigned; avoid var, because it is function-scoped and hoisted with undefined, which causes subtle bugs.

The short answer

  • const — block-scoped, cannot be reassigned. Use it by default.
  • let — block-scoped, can be reassigned. Use it for counters, accumulators and values that really change.
  • var — function-scoped, hoisted with the value undefined, can be redeclared. Avoid it in new code.
varletconst
Scopefunctionblockblock
Reassignyesyesno
Redeclare in same scopeyesnono
Before declarationundefinedReferenceError (TDZ)ReferenceError (TDZ)
Must be initializednonoyes

Function scope vs block scope

A block is anything in curly braces: if, for, while, or a bare { }. let and const live only inside the block. var ignores blocks and belongs to the whole function (or the global scope).

function demo() {
  if (true) {
    var a = 1;
    let b = 2;
  }
  console.log(a); // 1 — var leaked out of the if block
  console.log(b); // ReferenceError: b is not defined
}

The leak of var is the root of most problems: a variable you thought was local to a loop or condition is visible and changeable across the entire function.

Hoisting and the temporal dead zone

Hoisting means the engine registers declarations before executing the code of their scope. All three keywords are hoisted, but differently:

  • var is created and initialized with undefined right away.
  • let and const are created but not initialized. Until the line with the declaration runs, the variable is in the temporal dead zone (TDZ), and any access throws a ReferenceError.
console.log(x); // undefined
var x = 5;

console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 5;

The TDZ is a feature: reading a variable before it has a value is almost always a bug, and let/const report it loudly instead of silently giving you undefined.

Function declarations are hoisted fully, with their body, so you can call them above their definition. Function expressions assigned to const follow the TDZ rules.

The classic loop closure bug

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

There is only one i for the whole function. The callbacks run after the loop finishes, and by then i is 3.

With let, each iteration gets a fresh binding, so every callback captures its own value:

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

Before let existed, developers wrapped the body in an immediately invoked function to create a new scope. Today let solves it in one word.

const does not freeze objects

const protects the binding, not the value. You cannot assign a new object to the variable, but you can change the object it points to.

const user = { name: "Aziz" };
user.name = "Dilnoza";  // OK — the object is mutated
user = {};              // TypeError: Assignment to constant variable

const list = [1, 2];
list.push(3);           // OK

If you need an immutable object, use Object.freeze() (shallow) or design your code to create new objects instead of mutating.

A simple rule

  1. Write const first.
  2. Change it to let only when you actually need to reassign the variable.
  3. Do not use var in new code. Linters can enforce this (for example, the no-var and prefer-const rules in ESLint).

This also makes code easier to read: when you see let, you know the value changes somewhere below.

Common mistakes

  • Thinking const makes arrays and objects read-only.
  • Using var inside loops with asynchronous callbacks.
  • Declaring let inside a block and expecting it to be visible after the block.
  • Using a variable at the top of a function "because it is hoisted" — with let/const this throws.
  • Redeclaring a var by accident in a long function and overwriting a value.

FAQ

For practical purposes there is no meaningful difference. Choose by semantics and safety, not speed.

We can help with this

If you'd rather get the result than figure it all out yourself — this is what we do every day.

Discuss a project

Read next

Have an idea or a task? Tell us about it

We'll reply within one business day and propose a solution. The consultation is free.