Back to List

let, var, const β€” Differences in Variable Declaration

Clearly explains the differences between JavaScript's three variable declaration methods, var, let, and const,

Beginner
|
8min
|
Verified (2026-07)
letvarconstvariable declarationblock scopefunction scope
Progress0/55 (0%)

let, var, const – Variable Declaration Differences

After completing this topic, you will:

Be able to explain the differences between var, let, and const in terms of scope and reassignment, and be able to determine which one to use in different situations.


Three Declaration Keywords

In JavaScript, there are three ways to declare variables:

javascript
var name = "철수";    // ES5 (legacy)
let age = 25;        // ES6 (2015~)
const PI = 3.14159;  // ES6 (2015~)

All three accomplish the same thing: "creating a named container for a value." The differences lie in scope (how far it's visible) and reassignment (whether the value can be changed).


Reassignment

javascript
let count = 0;
count = 1;          // βœ… `let` allows reassignment

const MAX = 100;
MAX = 200;          // ❌ TypeError: Assignment to constant variable

const prevents reassignment after declaration. You cannot change the value once it's assigned. let and var allow reassignment.

Note: const means the "variable binding is immutable," not that the "value is immutable." The internal state of objects or arrays can still be modified:

javascript
const user = { name: "철수" };
user.name = "영희";  // βœ… Modifying the object's internal state is allowed
user = {};           // ❌ Changing the variable itself to a different value is not allowed

Scope – var vs. let/const

var has function scope. It ignores the blocks ({}) of if statements or for loops:

javascript
function example() {
  if (true) {
    var x = 10;
  }
  console.log(x);  // 10 – accessible outside the `if` block!
}

let/const have block scope. They only exist within the block ({}) where they are declared:

javascript
function example() {
  if (true) {
    let y = 10;
  }
  console.log(y);  // ReferenceError: y is not defined
}

Block scope is more intuitive. It only lives within the curly braces where it's declared and disappears outside of it.


Differences in for Loops

javascript
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3 – all refer to the same `i`

Because var ignores the for loop block, by the time the setTimeout functions execute, i has already reached 3.

javascript
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2 – each iteration has its own `i`

let creates a new block scope for each iteration, so each i exists independently. This is the most significant practical difference between let and var.


Hoisting

Hoisting is the behavior where declarations are conceptually "moved" to the top of their scope.

javascript
console.log(a);  // undefined (not an error!)
var a = 5;

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

var declarations are hoisted and initialized to undefined. Therefore, accessing them before declaration doesn't cause an error – which can make debugging harder.

let and const are also hoisted, but accessing them before initialization results in an error. This period is called the TDZ (Temporal Dead Zone). Raising an error is more helpful for debugging.


Practical Guidelines

javascript
// 1. `const` by default
const API_URL = "https://api.example.com";
const users = [];

// 2. Use `let` only when the value needs to change
let count = 0;
count++;

// 3. Don't use `var`
// (you might see it in legacy code)
KeywordScopeReassignmentHoistingWhen to Use
varFunctionβœ…Initialized to undefinedDon't use
letBlockβœ…TDZ (error)When the value changes
constBlock❌TDZ (error)Default

In modern JavaScript, use const by default, and switch to let only when reassignment is necessary. var exists for historical reasons, but there's no reason to use it in new code.


A Common Bug with Closures and var

The most famous bug caused by var's function scope:

javascript
// We want 5 buttons, each displaying a different number
for (var i = 0; i < 5; i++) {
  document.getElementById("btn" + i).onclick = function() {
    alert(i);  // All alert 5
  };
}

All buttons display 5. By the time the buttons are clicked, i has already reached 5, and all the callbacks refer to the same i.

javascript
// Fix it with `let`
for (let i = 0; i < 5; i++) {
  document.getElementById("btn" + i).onclick = function() {
    alert(i);  // 0, 1, 2, 3, 4
  };
}

let creates a new scope for each iteration, so each callback has its own i. Before ES6, you would use an IIFE (Immediately Invoked Function Expression) to work around this, but let makes that pattern unnecessary.


var/let/const in Function Declarations

There are also differences when creating functions:

javascript
// Function declaration – hoisted
greet();  // βœ… works
function greet() {
  console.log("Hello");
}

// `const` function expression – not hoisted
hello();  // ❌ ReferenceError
const hello = () => {
  console.log("Hello");
};

Function declarations can be called from anywhere in the code, but a function declared with const can only be called after its declaration. It's best to be consistent with one style in a project.


Differences in Global Scope

javascript
// In a browser environment
var globalVar = "I'm var";
let globalLet = "I'm let";

console.log(window.globalVar);  // "I'm var" – added to the `window` object
console.log(window.globalLet);  // undefined – not added to `window`

Variables declared with var in the global scope become properties of the window object. This can cause naming conflicts between libraries. let and const do not add variables declared in the global scope to the window object.


Common Mistakes and Solutions

javascript
// Mistake 1: Modifying the internal state of a `const` object is allowed
const config = { debug: true };
config.debug = false;    // βœ… Allowed – modifying the object's internal state
config = {};             // ❌ Not allowed – reassigning the variable

// Mistake 2: Using `const` in a `for` loop
for (const i = 0; i < 3; i++) {  // ❌ Error: Cannot reassign in `i++`
  console.log(i);
}

// `for...of` allows `const` (new binding on each iteration)
for (const item of [1, 2, 3]) {  // βœ…
  console.log(item);
}


Object.freeze – Making Truly Immutable Objects

If you want to prevent even internal modification of a const object:

javascript
const config = Object.freeze({
  apiUrl: "https://api.example.com",
  maxRetries: 3
});

config.apiUrl = "changed";    // Silently ignored (error in strict mode)
console.log(config.apiUrl);   // "https://api.example.com"

Object.freeze() prevents adding, modifying, or deleting properties of the object. However, it's a shallow freeze, meaning that the internal state of nested objects can still be modified.


When you see const in code, you immediately know that the value won't change. This predictability becomes increasingly important as a project grows.

πŸ’¬ Questions & Comments

0 comments

You can post without signing in. Guest comments cannot be edited or deleted by their author.

0/2000

Loading...