一覧へ

let, var, const — 変数宣言の違い

JavaScript の var, let, const の 3 つの変数宣言方法の違いを、スコープと再代入の観点から明確に説明します。

入門
|
8
|
検証済み (2026-07)
letvarconst変数宣言ブロックスコープ関数スコープ
進捗0/55 (0%)

let、var、const ― 変数宣言の違い

このトピックを終えると

varletconst の違いをスコープと再代入の観点から説明でき、どのような状況で何を使うべきかを判断できるようになります。


3つの宣言キーワード

JavaScript で変数を宣言する方法は3つあります。

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

これら3つは、「値を格納する名前を作成する」という点では同じです。違いは、スコープ(どこまで見えるか)と、再代入(値を変更できるか)にあります。


再代入の可否

javascript
let count = 0;
count = 1;          // ✅ let は再代入可能

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

const は宣言後に再代入できません。一度代入した値を変更することはできません。letvar は再代入可能です。

注意:const は「値が不変」ではなく、「変数バインディングが不変」です。オブジェクトや配列の内部は変更可能です。

javascript
const user = { name: "철수" };
user.name = "영희";  // ✅ オブジェクト内部の変更は可能
user = {};           // ❌ 変数自体を別の値に置き換えることは不可

スコープ — var vs let/const

var関数スコープです。if文やfor文のブロック({})を無視します。

javascript
function example() {
  if (true) {
    var x = 10;
  }
  console.log(x);  // 10 — ifブロックの外でもアクセス可能!
}

let/constブロックスコープです。宣言されたブロック({})内でのみ存在します。

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

ブロックスコープの方が直感的です。宣言した中括弧内でのみ有効で、外では消えます。


for文での違い

javascript
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// 出力: 3, 3, 3  — すべて同じ i を参照

var で宣言した i は for文ブロックを無視するため、setTimeout が実行されるときにはすでに i は 3 になっています。

javascript
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// 出力: 0, 1, 2  — 各反復ごとに新しい i

let は各反復ごとに新しいブロックスコープを作成するため、それぞれの i が独立して存在します。これが letvar の実質的に最も重要な違いです。


ホイスティング

ホイスティングとは、宣言がコードの最上部に引き上げられる現象です。

javascript
console.log(a);  // undefined (エラーではない!)
var a = 5;

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

var は宣言がホイスティングされ、undefined で初期化されます。そのため、宣言前にアクセスしてもエラーになりません。これはバグを見つけるのが難しくなります。

letconst もホイスティングされますが、初期化前にアクセスするとエラーを発生させます。この期間を TDZ (Temporal Dead Zone) と呼びます。エラーが発生する方がデバッグに有利です。


実務ガイドライン

javascript
// 1. 基本は const
const API_URL = "https://api.example.com";
const users = [];

// 2. 値が変わる場合にのみ let
let count = 0;
count++;

// 3. var は使わない
// (レガシーコードでのみ見られる)
キーワードスコープ再代入ホイスティング使用するタイミング
var関数undefined で初期化使わない
letブロックTDZ (エラー)値が変わるとき
constブロックTDZ (エラー)デフォルト

モダン JavaScript では、const をデフォルトとして使用し、再代入が必要な場合にのみ let に変更しますvar は歴史的な理由で残っているだけで、新しいコードで使う理由はありません。


クロージャーと var の有名なバグ

var の関数スコープが引き起こす最も有名なバグ:

javascript
// 5つのボタンにそれぞれ異なる数字を表示したい
for (var i = 0; i < 5; i++) {
  document.getElementById("btn" + i).onclick = function() {
    alert(i);  // すべて 5 を表示
  };
}

すべてのボタンが 5 を表示します。クリックする時点では i はすでに 5 になっており、すべてのコールバックが同じ i を参照しているためです。

javascript
// let に変更すると解決
for (let i = 0; i < 5; i++) {
  document.getElementById("btn" + i).onclick = function() {
    alert(i);  // 0, 1, 2, 3, 4
  };
}

let は各反復ごとに新しいスコープを作成するため、各コールバックがそれぞれ独自の i を持ちます。ES6 以前は IIFE (即時実行関数) で回避していましたが、let の登場によりこのパターンは不要になりました。


関数宣言における var/let/const

関数を作成するときにも、宣言方法によって違いがあります。

javascript
// 関数宣言 — ホイスティングされる
greet();  // ✅ 動作する
function greet() {
  console.log("こんにちは");
}

// const 関数式 — ホイスティングされない
hello();  // ❌ ReferenceError
const hello = () => {
  console.log("こんにちは");
};

関数宣言はコードのどこからでも呼び出すことができますが、const で宣言した関数は宣言以降でのみ呼び出すことができます。プロジェクトでは通常、一貫した方法を使用します。


グローバルスコープでの違い

javascript
// ブラウザ環境で
var globalVar = "私は var";
let globalLet = "私は let";

console.log(window.globalVar);  // "私は var" — window オブジェクトに追加される
console.log(window.globalLet);  // undefined — window に追加されない

var で宣言したグローバル変数は、window オブジェクトのプロパティになります。これはライブラリ間の名前の衝突の原因となります。letconst はグローバルスコープで宣言しても、window に追加されません。


よくある間違いと解決策

javascript
// 間違い 1: const オブジェクトの内部を変更することは可能
const config = { debug: true };
config.debug = false;    // ✅ 可能 — オブジェクト内部の変更
config = {};             // ❌ 不可能 — 変数の再代入

// 間違い 2: for文に const を使う
for (const i = 0; i < 3; i++) {  // ❌ i++ で再代入エラー
  console.log(i);
}

// for...of では const を使用可能 (各反復で新しいバインディング)
for (const item of [1, 2, 3]) {  // ✅
  console.log(item);
}


Object.freeze — 真に不変にする

const オブジェクトの内部を本当に変更できなくするには:

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

config.apiUrl = "changed";    // 静かに無視される (strict mode ではエラー)
console.log(config.apiUrl);   // "https://api.example.com"

Object.freeze() は、オブジェクトのプロパティの追加、変更、削除を防止します。ただし、浅いフリーズであるため、ネストされたオブジェクトの内部は引き続き変更可能です。


コードを読むときに const が見えれば、「この値は変わらないんだ」とすぐにわかります。この予測可能性は、プロジェクトが大きくなるほど重要になります。

💬 質問・コメント

0件のコメント

ログインせずに投稿できます。ゲスト投稿は投稿者自身で編集・削除できません。

0/2000

読み込み中...