更新履歴
- 【Git】複数のコミットを一つにまとめる|squash/fixupで履歴を整える
- ダックタイピングとは?「アヒルのように鳴くならアヒル」をわかりやすく解説
- crontabファイルの場所はどこ?OS別の保存先パスと確認・編集方法を徹底解説
- 【pytest】特定のテストだけを実行する方法!ファイル・クラス・関数ごとに解説
- TeraTermのセッションが勝手に切れる原因と対策|タイムアウトを防ぐ設定ガイド
- WinMergeをインストール不要で使う!ポータブル版の導入手順とメリットを解説
- 【完全ガイド】WinMergeでバイナリ比較をする方法
- SwaggerとOpenAPIの違いを徹底解説!仕様とツールの関係性を理解する
- 【Python】ファイル存在チェックの実装方法(pathlib、os.path)
- Pythonで文字列を除去する方法を完全解説!strip・replace・正規表現
- スタック領域とヒープ領域の違いとは?メモリ管理から使い分けまで徹底解説
- Python Docstringの書き方完全ガイド|主要スタイルの比較と保守性を高める記述
- シングルトン(Singleton)デザインパターンを徹底解説!Java実装例・メリット・デメリット
- サインインとログインの違いとは?意味・使い分けをわかりやすく解説
- 静的サイトと動的サイトの違いを徹底比較!メリット・デメリットと選び方を解説
- モノリスとマイクロサービスの違いを比較|メリット・デメリットと選定基準
- RESTとSOAPの違いを徹底比較!特徴・メリット・使い分けを解説
- 同期・非同期とブロッキング・ノンブロッキングの違い|4つの概念を徹底比較
- マルチプロセスとマルチスレッドの違いを解説!メリット・デメリット・使い分け
- hostsファイルとDNSの違いとは?優先順位・仕組み・使い分けを解説
お役立ちツール
JavaScript学習者にお勧めの本
JavaScriptで開発を始める際、最初に出会うのが変数宣言です。かつては var しかありませんでしたが、ES6(ES2015)以降、 let と const が登場し、現在はこれらを適切に使い分けることがデファクトスタンダードとなっています。
「どれを使えばいいの?」「巻き上げって具体的にどういうこと?」という疑問を解決するために、本記事ではそれぞれの特性を整理し、現場で推奨される使い分けについて解説します。
[!IMPORTANT] 本記事のポイント
- 基本は
constを使い、再代入が必要な場合のみletを選ぶvarはスコープや巻き上げの挙動が直感的ではなく、バグの原因になりやすいため、新規開発では原則使用しない- 「巻き上げ」と「TDZ(一時的デッドゾーン)」の仕組みを理解することで、予期せぬバグを防ぐ
1. 【比較表】var, let, const の違いと使い分け
まずは、各宣言方法の主な違いを比較表で確認しましょう。
| 項目 | var | let | const |
|---|---|---|---|
| 概要 | 旧来の変数宣言 | 再代入可能な変数 | 再代入できない変数 |
| スコープ | 関数スコープ | ブロックスコープ | ブロックスコープ |
| 再宣言 | 〇 | × | × |
| 再代入 | 〇 | 〇 | × |
| 巻き上げ | 〇 (undefined) | △ (TDZ) | △ (TDZ) |
| 推奨度 | 低 | 中 | 高 |
[!TIP] 現代のJavaScript開発では、「まずは
constを使い、再代入が必要な場合のみletを使う」 というのが鉄則です。varは歴史的経緯から存在する宣言方法であり、モダンな新規開発ではほとんど使用されません。ただし、古いライブラリやレガシーコードでは現在でも見かけることがあります。
2. 各宣言方法(var / let / const)の特徴と具体例
var の特徴と問題点
var はJavaScriptの初期から存在する宣言方法ですが、現代の開発では、スコープや巻き上げの挙動が原因でバグを生みやすい宣言方法とされています。
特徴:関数スコープと再宣言
var には「ブロックスコープ」という概念がなく、スコープは常に一番近い「関数単位」となります。また、同じスコープ内で何度でも再宣言が可能です。
// 【問題点1】再宣言を許容してしまうvar user = "Tanaka";var user = "Sato"; // エラーにならず上書きされる
// 【問題点2】ブロックスコープを無視(スコープの汚染)if (true) { var score = 100;}console.log(score); // 100 (ブロックの外からアクセスできてしまう)この「どこからでも見えてしまう」性質は、大規模なコードベースにおいて変数の競合や、予期せぬ場所での値の書き換えを引き起こすリスクがあります。
let の特徴と使い方
let は ES6 (ES2015) で導入された、安全性を重視した変数宣言方法です。
特徴:ブロックスコープと再宣言の禁止
let は { }(中括弧)で囲まれた「ブロック」内でのみ有効です。また、同じスコープ内での再宣言を禁止しているため、うっかり同じ名前の変数を作ってしまうミスを防げます。
注目点:ループ内での挙動
let の最大の強みの一つが、for ループ内での挙動です。
// let の場合:期待通り 0, 1, 2 と出力されるfor (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100);}
// var の場合:すべて 3 と出力されてしまう(クロージャが同じ変数を参照するため)for (var j = 0; j < 3; j++) { setTimeout(() => console.log(j), 100);}let を使うことで、非同期処理やコールバック関数においても変数の値を正しく保持できるようになります。
const の特徴と使い方
const は再代入できない変数を宣言するための仕組みです。変数そのものの参照は変更できませんが、オブジェクトや配列の中身を変更することは可能です。現代のJavaScriptでは、「変数はまず const で作る」 のが開発の鉄則です。
特徴:再代入の禁止
一度代入した値を書き換えることができません。これにより、コードを読む人はその変数が何を示しているかを常に信頼できるようになります。
注意点:オブジェクトと配列の「操作」
const は「変数そのものへの再代入」を禁止しますが、オブジェクトや配列の「中身(要素)」を書き換えることは可能です。
const colors = ["red", "blue"];colors.push("green"); // OK! (中身の追加)console.log(colors); // ["red", "blue", "green"]
// colors = ["white"]; // TypeError! (参照自体の入れ替えは不可)[!TIP] オブジェクトの中身(プロパティ)まで完全に変更不可能にしたい場合は、
Object.freeze()などのメソッドを併用する必要があります。
ただしObject.freeze()は浅い凍結(shallow freeze) のため、ネストしたオブジェクトまでは保護されない点に注意が必要です。 例えば次のような場合、ネストされたオブジェクトのプロパティは変更できてしまいます。const obj = {user: { name: "Tanaka" }};Object.freeze(obj);obj.user.name = "Sato"; // 変更できてしまう
3. 巻き上げ(Hoisting / ホイスティング)の仕組みとまとめ
JavaScriptの変数宣言には、コードの実行前に「スコープの先頭に持ち上げられる」ように見える巻き上げ(Hoisting / ホイスティング) という性質があります。
var の巻き上げ(undefined になる)
var は宣言が最上部に持ち上げられますが、値の初期化(代入)は実際の宣言行で行われます。そのため、宣言前にアクセスしてもエラーにはならず、中身が空である undefined が返ってきてしまいます。
console.log(x); // undefined (エラーにならないが混乱を招く)var x = 5;let / const の巻き上げと TDZ (一時的デッドゾーン)
let や const もスコープ生成時に変数は登録されますが、宣言行で初期化されるまで 未初期化状態(TDZ: Temporal Dead Zone) に置かれます。この期間に変数へアクセスすると ReferenceError が発生します。
TDZ とは?
TDZは、スコープの開始から初期化(宣言行への到達)までの間、その変数にアクセスできない「禁止区域」のことです。 この期間に変数へアクセスすると ReferenceError が発生します。
なぜ「Temporal(一時的)」なのか
TDZは単なるコードの記述順だけで決まるわけではなく、実際の実行順序 に基づいて判断されます。宣言行よりも物理的に「下」に書かれたコードであっても、宣言より「前」に実行される場合はエラーになります。
// 関数定義は物理的に「上」にあるが…function showMessage() { console.log(message);}
// ここで実行すると message はまだ宣言されていない(TDZ内)// showMessage(); // ReferenceError: Cannot access 'message' before initialization
let message = "Hello!";showMessage(); // ここならOK! ("Hello!")このように、TDZのおかげで「初期化されていない変数へのアクセス」という論理的なミスを、実行時に即座にエラーとして検出できるようになっています。
4. 実践:変数宣言にまつわる「よくあるバグ」とその対策
ここでは、JavaScript開発で実際に起こりやすいバグの例と、その回避策を紹介します。
① var による変数の「意図しない再宣言」
大規模なコードや長い関数内で var を使うと、すでに定義済みの変数をうっかり再宣言して上書きしてしまうことがあります。let や const であればエラーとして通知されるため、このミスを未然に防げます。
var status = "pending";// ...(長い処理)...var status = "success"; // ❌ 再宣言してもエラーにならず、値が上書きされる
let mode = "light";// ...let mode = "dark"; // ✅ エラー:Identifier 'mode' has already been declared② 宣言キーワードの付け忘れ(グローバル汚染)
const や let を付け忘れて変数に代入すると、非 strict mode の環境では JavaScriptはそれを自動的に「グローバル変数」として扱います。
ただし、'use strict' や ES Modules(モダンな環境)では、この書き方は ReferenceError: is not defined となり実行時エラーになります。
function setUser() { userName = "Tanaka"; // ❌ 宣言キーワードがないため、グローバル変数になる}
setUser();console.log(globalThis.userName); // "Tanaka" (意図せずどこからでもアクセス可能に)対策: 常に const または let を使用してください。また、モダンな環境(ES Modules)や 'use strict'; 指定下では、この書き方はエラーになります。
③ const オブジェクトの「中身」が書き換わる
const は「再代入」を禁止するだけで、オブジェクトや配列の「プロパティや要素」の変更までは禁止しません。「constだから安心」と油断していると、関数に渡した先で元データを書き換えられてしまう副作用に繋がります。
const config = { theme: "dark" };
function resetTheme(options) { options.theme = "light"; // ❌ const だが、プロパティは変更できてしまう}
resetTheme(config);console.log(config.theme); // "light" (元の値が書き換わった)なお const は「値を変更できない」のではなく 「変数の参照(binding)が再代入できない」 という意味です。
対策: 完全に変更を禁止(イミュータブルに)したい場合は、 Object.freeze() を活用するか、スプレッド構文などによるコピーを活用しましょう。
5. 実務における「使い分け」の判断基準
現場で迷わないための、具体的な使い分けルールを整理します。
① 原則として const をデフォルトにする
「後から値を書き換える必要がない」変数がほとんどです。const を使うことで、その変数がプログラムの途中で意図せず書き換わらないことが保証され、コードの可読性と保守性が飛躍的に向上します。
② 再代入が必要な場合のみ let を使う
以下のケースでは let を使用します。
- ループカウント:
for (let i = 0; i < 10; i++) - フラグや集計値: 途中で
true/falseを切り替えたり、数値を加算したりする場合 - 初期値が空の変数:
if文などの条件によって後から値を代入する場合
③ var は一切使用しない
現代のJavaScript(ES6以降)において、 var を使うメリットはありません。レガシーコードの修正でない限り、新規のコードで var を書くのは避けましょう。
④ スコープは最小限にする
変数は、使う直前で宣言するようにしましょう。関数の冒頭で全ての変数(特に let)を宣言してしまうと、どこで値が変わるかを追いかけるのが大変になります。必要なブロック内でのみ有効なスコープを作るのがベストプラクティスです。
6. まとめ:変数を適切に使い分けるために
バグの少ないクリーンなコードを書くために、JavaScriptの変数宣言を正しく理解することは第一歩です。
- 基本の優先順位は
const>let>var varはスコープや巻き上げの挙動が分かりにくく、バグの原因になりやすい- まず
constを使い、再代入が必要な場合のみletを選ぶ
JavaScript学習者にお勧めの本
以上で本記事の解説を終わります。
よいITライフを!