Skip to content
  • Variables: var, let & const · JavaScript

Variables: var, let & const

LearnViaNotes @learnvianotes· 3 min read

const by default, let when you must reassign, var never — and know why: scope, hoisting and the temporal dead zone.

The three declarations

varletconst
ScopeFunctionBlock { }Block { }
Hoisted?Yes, initialised to undefinedYes, but uninitialised (TDZ)Yes, but uninitialised (TDZ)
Re-declare in same scopeAllowedSyntaxErrorSyntaxError
ReassignYesYesNo — TypeError
Creates globalThis property (scripts)YesNoNo

Block scope vs function scope

JavaScript
function demo() {
  if (true) {
    var a = "var leaks out of the block";
    let b = "let stays inside";
  }
  console.log(a);
  console.log(typeof b);
}
demo();
Output
var leaks out of the block
undefined

const means constant binding, not immutable value

JavaScript
const user = { name: "Asha" };
user.name = "Ravi";          // fine: mutating the object
user.age = 30;               // fine
console.log(user);
try {
  user = {};                 // not fine: rebinding the name
} catch (e) {
  console.log(e.name + ": " + e.message);
}
Output
{ name: 'Ravi', age: 30 }
TypeError: Assignment to constant variable.

Hoisting and the Temporal Dead Zone

JavaScript
console.log(x);        // var is hoisted and set to undefined
var x = 5;

try {
  console.log(y);      // y exists but is in the TDZ
} catch (e) {
  console.log(e.name + ": " + e.message);
}
let y = 10;
Output
undefined
ReferenceError: Cannot access 'y' before initialization

The TDZ is temporal (about time, not position): a function that reads the variable is fine as long as it is called after the declaration runs.

JavaScript
function read() { return value; }  // refers to value declared below
let value = 42;
console.log(read());               // called after initialisation → OK
Output
42

The classic loop bug

JavaScript
const withVar = [];
for (var i = 0; i < 3; i++) withVar.push(() => i);
console.log(withVar.map((f) => f()));

const withLet = [];
for (let j = 0; j < 3; j++) withLet.push(() => j);
console.log(withLet.map((f) => f()));
Output
[ 3, 3, 3 ]
[ 0, 1, 2 ]

var i is one variable shared by every closure (it is 3 when they run). let j creates a fresh binding per iteration, so each closure captures its own value.

Naming rules

  • Letters, digits, _ and $; cannot start with a digit; case-sensitive (name ≠ Name).

  • Reserved words (class, return, new…) cannot be names.

  • Conventions: camelCase for variables/functions, PascalCase for classes, UPPER_SNAKE_CASE for true constants.

What is the difference between var, let and const?

Are let and const hoisted?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report