Skip to main content

The difference between let, const and var

let and const differ from var by being block scoped, by forbidding redeclaration and by using the Temporal Dead Zone instead of a silent undefined. On top of that const forbids reassignment, and neither let nor const adds a property to the global object in the browser, unlike var.

Theory

TL;DR

  • var is function or globally scoped; let and const are block scoped.
  • All three are hoisted, but var is initialised to undefined, while let and const sit in the TDZ and throw a ReferenceError.
  • Redeclaration is allowed only for var; for let and const it is a SyntaxError.
  • var and let can be reassigned; const throws a TypeError.
  • const protects the binding, not the contents: object properties and array elements can still change.
  • In the browser a top level var becomes a property of window, let and const do not.

Quick example

javascript
function demo() { if (true) { var scopedToFunction = 'var'; let scopedToBlock = 'let'; const alsoScopedToBlock = 'const'; } console.log(scopedToFunction); // 'var', visible in the whole function console.log(scopedToBlock); // ReferenceError console.log(alsoScopedToBlock); // ReferenceError } demo();

The main differences

Criterionvarletconst
ScopeFunction or globalBlock ({ ... })Block ({ ... })
HoistingHoisted and initialised to undefinedHoisted but not initialised (error when touched before the declaration)Hoisted but not initialised (error when touched before the declaration)
RedeclarationAllowed in the same scopeErrorError
ReassignmentAllowedAllowedNot allowed
Added to the window object (in the browser)Yes (window.varName)NoNo

Scope

var is function scoped. An if block, a for block or a bare { ... } is no barrier for it: the variable belongs to the nearest function or to the global scope.

javascript
if (true) { var a = 10; } console.log(a); // 10 (available outside the block)

let and const are block scoped. They exist only inside the curly braces they were declared in.

javascript
if (true) { let b = 20; const c = 30; } console.log(b); // ReferenceError console.log(c); // ReferenceError

let and const only "live" inside the {} block.

This is exactly why a for loop with let gives every iteration its own variable, while var gives one shared variable for the whole loop:

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

Hoisting and the Temporal Dead Zone

Every variable is "hoisted" to the top of its scope, but in different ways. var is immediately initialised to undefined, while let and const land in the Temporal Dead Zone (TDZ), a temporary dead zone where the variable already exists but cannot be touched yet.

javascript
console.log(a); // undefined (var is hoisted and initialised) var a = 5; console.log(b); // ReferenceError (b is in the TDZ) let b = 10; console.log(c); // ReferenceError (c is in the TDZ) const c = 15;

The TDZ is not a flaw but a safeguard: instead of a silent undefined that shows up later as a strange bug, you get an error on the exact line where the mistake was made.

Redeclaration, reassignment and const with objects

Only var lets you redeclare a variable in the same scope. Reassignment works for var and let, but not for const.

javascript
var x = 1; var x = 2; // allowed console.log(x); // 2 let y = 1; let y = 2; // SyntaxError: Identifier 'y' has already been declared const z = 1; z = 2; // TypeError: Assignment to constant variable

An important nuance: const protects the binding, not the contents. The object or array the constant points at stays mutable.

javascript
const user = { name: 'Tim' }; user.name = 'Alex'; // the property can be changed console.log(user.name); // 'Alex' user = {}; // TypeError: the object itself cannot be reassigned

When you need the contents to be genuinely immutable, use Object.freeze(user).

The global object and the rule of thumb

In the browser a top level var creates a property on the global object, let and const do not.

javascript
var x = 10; let y = 20; const z = 30; console.log(window.x); // 10 console.log(window.y); // undefined console.log(window.z); // undefined

var adds the variable to the global object (window, globalThis), which makes it easy to overwrite someone else's global by accident. let and const do not, which makes the code safer.

SituationWhat to useWhy
The variable must changeletSafer than var, block scoped
The variable must not changeconstStates immutability explicitly
Legacy code (before ES6)varObsolete, only for supporting old projects

A simple rule to remember:

const by default. let if the value has to change. var never in modern code.

Common mistakes

  • Thinking const makes an object immutable. Only the binding of the name to the value is fixed; object fields and array elements change quite happily.
  • Believing let and const are not hoisted. They are hoisted, they just stay in the TDZ until the declaration line, hence a ReferenceError rather than undefined.
  • Using var in a loop with asynchronous callbacks. One shared variable for the whole loop produces the classic 3, 3, 3 instead of 0, 1, 2.
  • Relying on var "just working" outside a block. That is a source of accidental overwrites and name collisions in a large file.
  • Expecting window.myLet in the browser. let and const variables never reach the global object, so such access yields undefined.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.