Variable Declarations

JavaScript has three main ways to declare variables:

  • var
  • let
  • const

They all declare variables, but they behave differently in scope, hoisting, and reassignment.

var

var is the oldest declaration style in JavaScript, and it has several well-known problems.

Function scope

var is function-scoped, not block-scoped.

if (true) {
var x = 10;
}
console.log(x); // 10

Hoisting

var declarations are hoisted to the top of their function or global scope.

console.log(a); // undefined
var a = 5;

JavaScript treats that roughly like:

var a;
console.log(a);
a = 5;

Re-declaration

var allows redeclaration:

var name = "Tom";
var name = "Alice";

let

let was introduced in ES6 and fixes many of var’s problems.

Block scope

if (true) {
let x = 10;
}
// console.log(x); // ReferenceError

Temporal Dead Zone

let is hoisted, but it cannot be used before the declaration line.

// console.log(a); // ReferenceError
let a = 5;

No redeclaration

let value = 1;
// let value = 2; // SyntaxError

const

const has the same block scope rules as let, but adds stronger assignment rules.

Must be initialized

const pi = 3.14;

Cannot be reassigned

const name = "Tom";
// name = "Alice"; // TypeError

Special case: objects and arrays

const prevents reassignment of the variable binding, but it does not make the object or array itself immutable.

const user = { name: "Tom" };
user.name = "Alice"; // allowed
const arr = [1, 2, 3];
arr.push(4); // allowed

Which one should you use?

Priority

  1. Use const by default
  2. Use let if reassignment is necessary
  3. Avoid var unless you are maintaining old code

In practice

This rule keeps code easier to read and reduces accidental state changes.

Comparison Summary

DeclarationScopeHoisting behaviorRedeclarationReassignment
varFunction scopeHoisted and initialized to undefinedAllowedAllowed
letBlock scopeHoisted but in TDZNot allowedAllowed
constBlock scopeHoisted but in TDZNot allowedNot allowed

Common Pitfall

Closures inside loops

Using var in a loop often causes closure issues because all callbacks share the same variable.

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

With let, each loop iteration gets its own binding:

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

In modern JavaScript, correct use of let and const makes code safer and more predictable.