JavaScriptにおける変数宣言は、言語の進化とともにその役割が劇的に変化してきました。
かつてはvar一択だった時代もありましたが、現在のモダンな開発現場において変数のスコープ管理と意図の明確化は、バグの少ないコードを書くための基本中の基本です。
本記事では、2026年の標準的な開発スタイルに即したvar、let、constの使い分けについて、具体的なコード例を交えて詳しく解説します。
変数宣言の基本概念と現在の立ち位置
JavaScriptでプログラムを書く際、データを格納する箱としての役割を果たすのが変数です。
ES6 (ECMAScript 2015) の登場以前は、変数宣言にはvarしか存在しませんでしたが、現在はletとconstが主流となっています。
2026年現在、varを使用する機会はほとんどありません。これは、varが持つ「関数スコープ」や「変数の巻き上げ」といった仕様が、大規模なアプリケーション開発において予期せぬ挙動を引き起こす原因となりやすいためです。
現在のコーディング規約では、「原則としてconstを使用し、再代入が必要な場合のみletを使用する」という方針が世界的なスタンダードとなっています。
3つの宣言キーワードの比較表
まずは、それぞれのキーワードの違いを一覧表で確認しましょう。
| 特徴 | var | let | const |
|---|---|---|---|
| スコープ | 関数スコープ | ブロックスコープ | ブロックスコープ |
| 再代入 | 可能 | 可能 | 不可能 |
| 再宣言 | 可能 | 不可能 | 不可能 |
| 巻き上げ | あり (undefined) | あり (参照不可) | あり (参照不可) |
| 初期値の省略 | 可能 | 可能 | 必須 |
スコープの違い:関数スコープとブロックスコープ
プログラミングにおける「スコープ」とは、その変数がどこから参照可能かという有効範囲のことです。
この理解が不足していると、変数の値が意図せず書き換わってしまうなどのトラブルが発生します。
varが持つ関数スコープの罠
varは「関数スコープ」を持ちます。
これは、if文やfor文などのブロック内で宣言しても、その外側からアクセスできてしまうことを意味します。
// varのスコープ挙動
function varScopeTest() {
if (true) {
var x = "Hello, var";
}
// if文の外でもアクセスできてしまう
console.log(x);
}
varScopeTest();
Hello, var
この挙動は、コードが複雑になった際に、どこで変数が定義されたかを追いかけにくくする要因となります。
letとconstが持つブロックスコープ
対して、letとconstは「ブロックスコープ」を持ちます。
{} で囲まれた範囲内でのみ有効であるため、現代的なプログラミングの直感に合致した安全な設計が可能です。
// let/constのスコープ挙動
function blockScopeTest() {
if (true) {
let y = "Hello, let";
const z = "Hello, const";
console.log(y); // ブロック内ならOK
}
// ここでアクセスしようとするとエラー
try {
console.log(y);
} catch (e) {
console.log("Error: y is not defined");
}
}
blockScopeTest();
Hello, let
Error: y is not defined
このように、スコープを最小限に限定できることが、letやconstが推奨される最大の理由の一つです。
再代入と再宣言のルール
次に、宣言した変数を後から書き換えられるかどうか、という点について見ていきましょう。
constは「定数」ではなく「再代入禁止」
constは constant (定数) の略ですが、厳密には「値が不変である」ことではなく、「変数名への再代入を禁止する」ことを意味します。
そのため、一度代入した値を別の値に置き換えることはできません。
const userCount = 10;
// userCount = 11; // これはエラーになる
しかし、オブジェクトや配列のプロパティを変更することは可能である点に注意が必要です。
const user = { name: "Alice", age: 25 };
// オブジェクトそのものの再代入は不可
// user = { name: "Bob" }; // エラー
// プロパティの書き換えは可能
user.age = 26;
console.log(user.age);
26
letによる柔軟な再代入
ループのカウンター変数や、条件によって内容を書き換えたい場合はletを使用します。
let status = "pending";
if (isCompleted) {
status = "done";
}
ここで重要なのは、「不必要な再代入は避ける」という設計思想です。
コードを読む側にとって、constで宣言された変数は「後で値が変わらない」という保証があるため、読み解く際の認知的負荷が大幅に軽減されます。
巻き上げ(Hoisting)とTemporal Dead Zone (TDZ)
JavaScriptには、変数の宣言をスコープの先頭に「引き上げる」ような動作(巻き上げ)が存在します。
varの巻き上げ
varで宣言された変数は、宣言文より前で参照してもエラーにならず、undefinedが返されます。
console.log(greet); // エラーにはならない
var greet = "Good morning";
undefined
let/constとTDZ
letやconstも内部的には巻き上げが行われていますが、宣言の前にアクセスしようとすると ReferenceError が発生します。
この「宣言はされているがアクセスできない期間」をTemporal Dead Zone (TDZ: 一時的死域)と呼びます。
try {
console.log(message); // 初期化前にアクセス
let message = "Hello";
} catch (e) {
console.log(e.name + ": " + e.message);
}
ReferenceError: Cannot access 'message' before initialization
この仕様により、「変数は使う前に宣言する」という健全なコーディング習慣が強制されます。
2026年現在の推奨コーディング規約
最新のJavaScript開発において、どのようにこれらを使い分けるべきか、ベストプラクティスをまとめます。
1. デフォルトはconstを使用する
作成する変数のうち、8割から9割はconstで宣言できるはずです。
再代入の必要がないものはすべてconstにすることで、意図しないバグの混入を防ぎます。
2. 再代入が必要な場合のみletを使用する
主に以下のケースではletが適切です。
forループ内でのカウンター変数- 数学的な計算で値を更新していく場合
- 条件分岐によって、異なる初期値をセットしたい場合
3. varは決して使用しない
2026年において、新規のコードでvarを使用する理由は存在しません。
レガシーコードの保守などで見かけた場合は、慎重にリファクタリングを検討すべき対象です。
4. 適切な命名とスコープの最小化
変数宣言キーワードの選択と同じくらい重要なのが、「変数の寿命を短く保つ」ことです。
広すぎるスコープは、それだけでコードの可読性を下げます。
可能な限り、変数は使用する直前で宣言し、必要最小限のブロック内に閉じ込めるようにしましょう。
具体的な実務でのコード例
実際の開発シーンを想定した使い分けの例を紹介します。
配列の操作とconst
配列を操作する場合、要素の追加(push)などは「再代入」ではないため、constのまま実行できます。
// 配列の宣言(再代入しないのでconst)
const colors = ["red", "blue"];
// 要素の追加は再代入ではないため可能
colors.push("green");
console.log(colors);
// colors = ["black"]; // これは再代入なのでエラー
["red", "blue", "green"]
ループ処理におけるlet
ループ処理では、繰り返しごとに値が変化する変数にletを使用します。
const items = ["Apple", "Banana", "Cherry"];
// i はループごとに変化するためletを使用
for (let i = 0; i < items.length; i++) {
console.log(`Item ${i}: ${items[i]}`);
}
// for...of文であれば、各要素をconstで受け取れる
for (const item of items) {
console.log(item);
}
Item 0: Apple
Item 1: Banana
Item 2: Cherry
Apple
Banana
Cherry
for...of 文の中で const item を使用できるのは、各ループの反復ごとに新しいブロックスコープが作成され、新しい変数が宣言される仕組みだからです。
これも変数の不変性を高める有効な手法です。
注意すべき特殊な挙動とトラブルシューティング
最後に、開発中に陥りやすい注意点について触れておきます。
グローバルオブジェクトへの登録
ブラウザ環境において、グローバルスコープ(関数の外)でvarを使って宣言すると、それは window オブジェクトのプロパティになります。
var globalVar = "I am global";
console.log(window.globalVar); // "I am global"
しかし、letやconstで宣言した場合は、グローバルスコープであってもwindowオブジェクトのプロパティにはなりません。
let globalLet = "I am restricted";
console.log(window.globalLet); // undefined
これにより、既存のグローバル変数(nameやlocationなど)を意図せず上書きしてしまうリスクを回避できます。
定数の命名慣習
再代入不可のconstの中でも、特に「アプリケーション全体で共有する設定値(マジックナンバーの回避など)」については、大文字のアンダースコア記法(スネークケース)を用いることが一般的です。
const API_RETRY_COUNT = 3;
const DEFAULT_TIMEOUT_MS = 5000;
一方で、通常の変数(単に再代入しないだけのローカル変数)には、キャメルケース(userNameなど)を使用します。
まとめ
JavaScriptの変数宣言におけるvar、let、constの使い分けは、単なる文法の選択ではなく、「コードの堅牢性と意図をいかに伝えるか」という設計の根幹に関わる部分です。
- const:デフォルトで使用。再代入を禁止し、コードの予測可能性を高める。
- let:カウンターやフラグなど、再代入が必要な場面に限定して使用。
- var:現代の開発では使用しない。
2026年のエンジニアにとって、これらの適切な使い分けは、セキュアでメンテナンス性の高いアプリケーションを構築するための第一歩です。
常にconstを優先し、変数のスコープを最小限に留める意識を持つことで、チーム開発においても「読みやすく、壊れにくい」優れたソースコードを記述できるようになります。
今後、言語のさらなる進化により新しい宣言方法が登場する可能性もありますが、「不変性(Immutability)を重視する」という現在のトレンドは、今後も長く続いていくでしょう。
基礎をしっかりと固め、日々のコーディングに活かしてください。
