JavaScript var vs let vs const | Scope, Hoisting, and When to Use Each
이 글의 핵심
var vs let vs const: scope, hoisting, reassignment, and practical choice rules for modern JavaScript.
Introduction
“Should I never use var?” is one of the first questions JavaScript learners ask. This article clarifies var, let, and const and gives practical rules for modern code.
What you will learn
- Scope differences
- How hoisting differs (including the TDZ)
- Reassignment vs redeclaration
- When to pick each keyword
Table of contents
- Quick comparison
- Scope
- Hoisting
- Reassignment and redeclaration
- const “immutability”
- Practical guide
- Common mistakes
- Global object
- Closing thoughts
1. Quick comparison
| var | let | const | |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisting | Declared + initialized undefined | Declared (TDZ) | Declared (TDZ) |
| Reassign | Yes | Yes | No |
| Redeclare | Yes | No | No |
| Global object | Creates property on window (browser) | No | No |
| Default choice | Avoid | When reassignment needed | Default |
2. Scope
var: function scope
function testVar() {
if (true) {
var x = 10;
}
console.log(x); // 10 — visible outside the block
}
testVar();
let / const: block scope
function testLet() {
if (true) {
let x = 10;
const y = 20;
}
console.log(x); // ReferenceError
console.log(y); // ReferenceError
}
Loop pitfall
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // 3, 3, 3
}, 100);
}
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // 0, 1, 2
}, 100);
}
Why: var shares one binding across iterations; let creates a new binding per iteration.
3. Hoisting
var: hoisted and initialized to undefined
console.log(x); // undefined
var x = 10;
let / const: temporal dead zone (TDZ)
console.log(x); // ReferenceError
let x = 10;
4. Reassignment and redeclaration
var x = 10;
var x = 20; // allowed
let y = 10;
let y = 20; // SyntaxError
const z = 10;
z = 20; // TypeError
5. const immutability
const only fixes the binding—object contents can still change unless you freeze them.
const obj = { name: 'Alice' };
obj.name = 'Bob'; // OK
const arr = [1, 2, 3];
arr.push(4); // OK
Use Object.freeze (and deep freeze for nested objects) for true immutability when needed.
6. Practical guide
const API_URL = 'https://api.example.com';
const users = [];
let count = 0;
for (let i = 0; i < 10; i++) {
count += i;
}
ESLint
{
"rules": {
"no-var": "error",
"prefer-const": "warn",
"no-const-assign": "error"
}
}
7. Common mistakes
Accidental var redeclaration
var user = 'Alice';
// ...
var user = 'Bob'; // silent overwrite
Loop closures with var
Use let or bind with an IIFE—let is simplest.
Thinking const deep-freezes objects
It does not—only the variable binding.
8. Global object
var globalVar = 'x';
console.log(window.globalVar); // defined in browsers
let globalLet = 'y';
console.log(window.globalLet); // undefined
Accidental shadowing of globals (e.g. alert) is one reason var on globals is risky.
Closing thoughts
- Default to
const - Use
letwhen the binding must change - Avoid
varin new code - Enforce with ESLint
Priority: const > let > var.
FAQ
See frontmatter FAQ.
Related posts
- JavaScript scope and closure
- JavaScript hoisting
- Modern JavaScript syntax
Checklists
Declarations
- Prefer const
- let if reassignment needed
- No var
- no-var in ESLint
Review
- No accidental var
- let → const if never reassigned
- Loop indices use let
Keywords
JavaScript, var, let, const, scope, hoisting, TDZ, block scope, function scope, comparison