Blog
3 min read

var vs let vs const in JavaScript: Which to Use and Why

Three ways to declare a variable in JavaScript, and why modern code uses const by default, let when the value changes, and var almost never. Scope, hoisting, reassignment vs mutation, and the classic loop bug — with short examples.

JavaScript has three keywords for creating a variable: var, let and const. The modern rule fits on one line:

Use const by default. Use let when you need to reassign. Don't use var.

Here's why.

const: can't be reassigned

const price = 20
price = 25 // TypeError: Assignment to constant variable.

Use const for anything that won't be pointed at a new value — which is most variables. It makes code easier to read: you know the value won't change further down.

const doesn't mean "frozen"

This trips up everyone once:

const cart = ['apple']
cart.push('pear')     // fine — the array itself changed
cart = ['banana']     // error — can't point cart at a new array

const stops reassignment, not mutation. The array or object it holds can still be changed. (Object.freeze() exists if you truly need immutability.)

let: can be reassigned

let attempts = 0
attempts = attempts + 1 // fine

Use let for counters, values that change in a loop, or a variable you set in an if branch.

var: the old way

var was the only option before 2015. It still works, but it has two behaviours that cause bugs.

1. Function scope instead of block scope

let and const exist only inside the nearest { } block. var ignores blocks and belongs to the whole function:

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

2. Hoisting to undefined

var declarations are moved to the top of the function and start as undefined, so using one too early silently gives undefined:

console.log(total) // undefined (no error!)
var total = 10

With let or const, the same code throws an error — which is what you want, because it's a bug. (is not defined errors)

The classic loop bug

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

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

With let, each loop iteration gets its own i. With var, they all share one, which has reached 3 by the time the timers fire.

Quick reference

var let const
Scope Function Block Block
Reassign Yes Yes No
Redeclare in same scope Yes No No
Use before declaration undefined Error Error
Use in new code? No When reassigning By default

In practice

  • Linters like ESLint can enforce this automatically with the no-var and prefer-const rules. (Linters and formatters explained)
  • In React components, almost everything is const — including state from useState, because you change it with the setter, not by reassigning. (React useState explained)
  • If AI-generated code uses var, it's often copied from an old pattern. Safe to switch to let/const, but run the code afterwards.

EasySpawn gives you a server where your JavaScript app runs, with Claude Code ready to explain and tidy any code you didn't write yourself. See how it works or join the waitlist.

Related: JavaScript vs TypeScript · Async/Await in JavaScript · JavaScript Array Methods · HTML, CSS, and JavaScript Explained

Keep reading