JavaScript var vs let vs const | Scope, Hoisting, and When to Use Each

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

  1. Quick comparison
  2. Scope
  3. Hoisting
  4. Reassignment and redeclaration
  5. const “immutability”
  6. Practical guide
  7. Common mistakes
  8. Global object
  9. Closing thoughts

1. Quick comparison

varletconst
ScopeFunctionBlockBlock
HoistingDeclared + initialized undefinedDeclared (TDZ)Declared (TDZ)
ReassignYesYesNo
RedeclareYesNoNo
Global objectCreates property on window (browser)NoNo
Default choiceAvoidWhen reassignment neededDefault

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

  1. Default to const
  2. Use let when the binding must change
  3. Avoid var in new code
  4. Enforce with ESLint

Priority: const > let > var.


FAQ

See frontmatter FAQ.


  • 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