JavaScriptにおけるプログラミングにおいて、変数は最も基本的かつ重要な概念の一つです。
単に値を格納する箱としての役割だけでなく、コードの可読性や保守性、さらにはプログラムの実行効率にまで大きな影響を及ぼします。
2026年現在のモダンなフロントエンドおよびバックエンド開発では、変数の宣言方法一つをとっても、厳密なルールとベストプラクティスが存在します。
本記事では、初心者から中級者までが曖昧に捉えがちな「let」と「const」の使い分けや、JavaScript特有のスコープの仕組みについて、最新の標準仕様に基づき詳しく解説していきます。
変数宣言の基本とその役割
プログラミングにおける変数とは、データに名前を付けてメモリ上に保存し、後から参照や再利用を可能にするための仕組みです。
JavaScriptは動的型付け言語であるため、変数に格納するデータの種類(型)を明示的に指定する必要はありませんが、その宣言方法にはいくつかの種類があります。
現在、推奨されている宣言方法は const と let の2種類です。
以前は var が主流でしたが、現在の開発現場で var を使用することは非推奨とされています。
なぜ変数宣言が必要なのか
JavaScriptでは、宣言せずに変数を使用することも技術的には可能(非厳密モード時)ですが、これは予期せぬバグの温床となります。
変数宣言を行うことで、以下のメリットを享受できます。
- コードの意図の明確化:そのデータが変更されるものか、不変のものかを宣言時に示せます。
- メモリ管理の最適化:エンジンのガベージコレクションが適切に機能しやすくなります。
- エラーの早期発見:未定義の変数へのアクセスを制限し、実行時のエラーを防ぎます。
var・let・const の違いと歴史的背景
JavaScriptの変数の歴史を振り返ると、かつては var しか存在しませんでした。
しかし、var には「再宣言が可能である」「スコープが広すぎる」といった問題があり、大規模なアプリケーション開発には不向きでした。
そこでES6(ECMAScript 2015)以降、新しく let と const が導入されました。
これら三者の違いを以下の表にまとめます。
| 項目 | var | let | const |
|---|---|---|---|
| スコープ | 関数スコープ | ブロックスコープ | ブロックスコープ |
| 再宣言 | 可能 | 不可能 | 不可能 |
| 再代入 | 可能 | 可能 | 不可能 |
| ホイスティング | あり (undefined) | あり (TDZ発生) | あり (TDZ発生) |
現在では、「基本はconstを使い、再代入が必要な場合のみletを使う」というスタイルが世界共通のスタンダードとなっています。
const:定数としての活用
const は「Constant(定数)」の略であり、一度値を代入すると、その後に別の値を再代入することができない変数を宣言する際に使用します。
const の基本的な使い方
以下の例では、定数に対して再代入を試みた際にエラーが発生する様子を示しています。
// constによる宣言
const MAX_USERS = 100;
console.log(MAX_USERS);
// 再代入を試みる
try {
MAX_USERS = 200; // TypeErrorが発生する
} catch (error) {
console.log("エラー発生:", error.message);
}
100
エラー発生: Assignment to constant variable.
参照型(オブジェクト・配列)における注意点
ここで注意が必要なのは、const が保証するのは「変数そのものへの再代入の禁止」であり、「値の不変性(イミュータビリティ)」ではないという点です。
オブジェクトや配列を const で宣言した場合、その中身のプロパティや要素を書き換えることは可能です。
const userProfile = {
name: "田中",
age: 25
};
// プロパティの変更は可能
userProfile.age = 26;
console.log(userProfile.age);
// 配列の操作も可能
const colors = ["red", "blue"];
colors.push("green");
console.log(colors);
// 変数そのものの置き換えは不可能
// userProfile = {}; // これはエラーになる
26
["red", "blue", "green"]
2026年の開発環境では、もし中身も完全に固定したい場合は、ECMAScriptの新しい機能である Records & Tuples(提案仕様)や、Object.freeze() メソッドを併用することが推奨されます。
let:再代入が必要なケース
let は、プログラムの実行過程で値が変化する変数を宣言するために使用します。
例えば、ループ処理のカウンタ変数や、条件によって中身を書き換えたいフラグ変数などが該当します。
let の基本的な使い方
let counter = 0;
counter += 1; // 再代入可能
console.log("現在のカウント:", counter);
let message;
if (counter > 0) {
message = "1以上です";
} else {
message = "0以下です";
}
console.log(message);
現在のカウント: 1
1以上です
let を使用する際の重要なポイントは、同じスコープ内で同じ名前の変数を二度宣言できないことです。
これにより、意図せず既存の変数を上書きしてしまうミスを防ぐことができます。
変数のスコープ(生存範囲)を理解する
スコープとは、変数が有効な範囲(参照できる範囲)のことです。
JavaScriptには大きく分けて「グローバルスコープ」「関数スコープ」「ブロックスコープ」の3種類が存在します。
グローバルスコープ
プログラムのどこからでもアクセスできる範囲です。
関数の外側で宣言された変数はグローバル変数となり、ブラウザ環境では window オブジェクト、Node.js環境では global オブジェクトに関連付けられることがあります。
ただし、グローバル変数の多用は名前の衝突を引き起こすため、最小限に抑えるべきです。
関数スコープ
var で宣言された変数が持つスコープです。
関数の中で宣言された変数は、その関数の外からはアクセスできません。
function testFunction() {
var localVal = "内部";
console.log(localVal);
}
testFunction();
// console.log(localVal); // ReferenceError: localVal is not defined
ブロックスコープ(重要)
let と const が採用しているのが、このブロックスコープです。
if文、for文、switch文などの { } (波括弧)で囲まれた範囲内でのみ変数が有効となります。
if (true) {
let blockScoped = "ブロック内のみ";
const alsoBlockScoped = "これもブロック内のみ";
console.log(blockScoped);
}
// ここでアクセスするとエラーになる
// console.log(blockScoped);
この仕様により、ループ内の一時的な変数が外部に漏れ出す心配がなくなり、堅牢なコードを記述できるようになりました。
ホイスティング(巻き上げ)とTDZの仕組み
JavaScriptには「ホイスティング(巻き上げ)」という特有の挙動があります。
これは、変数の宣言が、そのスコープの先頭で行われたかのように扱われる現象です。
var の巻き上げ
var で宣言した場合、宣言前に変数にアクセスしてもエラーにならず、undefined が返されます。
console.log(hoistedVar); // undefined
var hoistedVar = "Hello";
let / const と TDZ(Temporal Dead Zone)
let と const も内部的には巻き上げが発生していますが、初期化される前にアクセスすると ReferenceError が発生します。
この「宣言から初期化までのアクセス不可能な範囲」を Temporal Dead Zone(一時的死区) と呼びます。
try {
console.log(tdzVar); // ReferenceErrorが発生
let tdzVar = "Hi";
} catch (e) {
console.log("エラー:", e.name);
}
エラー: ReferenceError
この仕組みがあるおかげで、開発者は「変数は使う前に宣言する」という健全なコーディング習慣を強制され、バグの混入を防ぐことができます。
変数名の命名規則とベストプラクティス
2026年においても、変数名の付け方はコードの品質を左右する重要な要素です。
プロジェクトごとに規約は異なりますが、JavaScriptコミュニティにおける一般的なルールは以下の通りです。
基本的なルール
- キャメルケース(camelCase):
userName,totalPriceのように、2単語目以降の頭文字を大文字にします。 - 意味のある名前:
aやdataなどの抽象的な名前ではなく、userEmailやisLoggedInのように、中身が推測できる名前にします。 - 真偽値のプレフィックス:
is,has,can,shouldなどで始めることで、その変数が boolean 型であることを示唆します。
定数の扱い
再代入を行わない const 変数のうち、特に設定値(マジックナンバーの回避)として扱うものは、アッパースネークケースで記述するのが慣例です。
const API_RETRY_COUNT = 3;
const THEME_COLOR_PRIMARY = "#007bff";
これにより、プログラム全体で共通して使われる「不変の設定」であることが一目でわかります。
変数とデータ型:プリミティブ型と参照型
変数に格納されるデータには、大きく分けて「プリミティブ型」と「参照型」があります。
これらを理解していないと、変数のコピーや代入時に予期せぬ挙動に悩まされることになります。
プリミティブ型(値渡し)
数値、文字列、真偽値、null、undefined、Symbol、BigIntが該当します。
これらは変数に「値そのもの」が格納されます。
let a = 10;
let b = a; // 値がコピーされる
b = 20;
console.log(a); // 10 のまま
参照型(参照渡し)
オブジェクト、配列、関数が該当します。
変数にはメモリ上の「アドレス情報(参照)」が格納されます。
let obj1 = { name: "Alice" };
let obj2 = obj1; // 参照(アドレス)がコピーされる
obj2.name = "Bob";
console.log(obj1.name); // "Bob" に書き換わっている
Bob
参照型のこの挙動は、JavaScriptで最も多いバグの原因の一つです。 意図せず元のデータを書き換えないためには、スプレッド構文 ... や structuredClone() を使用して、ディープコピー(深いコピー)を行う必要があります。
モダンJavaScriptにおける変数の展開と分割代入
2020年代半ばの現在、変数の扱いをより効率的にする手法が広く使われています。
分割代入(Destructuring assignment)
オブジェクトや配列から必要な値を抽出し、個別の変数に代入する簡潔な書き方です。
const user = { id: 1, name: "佐藤", email: "sato@example.com" };
// 必要なプロパティだけを変数として取り出す
const { name, email } = user;
console.log(name, email);
const colors = ["red", "green", "blue"];
const [primary, secondary] = colors;
console.log(primary, secondary);
テンプレートリテラル
文字列の中に変数を取り込む際、以前は + 演算子で結合していましたが、現在はバッククォート <code> </code> を使用したテンプレートリテラルが主流です。
const product = "ノートPC";
const price = 120000;
// 変数の埋め込み
console.log(`${product}の価格は${price.toLocaleString()}円です。`);
ノートPCの価格は120,000円です。
2026年における最新の変数事情
2026年現在のJavaScript環境(ES2025以降を含む)では、非同期処理の進化や、より厳格な型安全性を求める傾向が強まっています。
- Top-level await:モジュールのトップレベルで直接変数を
awaitできるようになり、非同期で初期化される定数の扱いが容易になりました。 - Private Fields:クラス構文内での変数(プロパティ)に
#を付けることで、完全なカプセル化(外部からのアクセス遮断)が可能になっています。 - Type Annotations(提案段階):TypeScriptのような型注釈をコメントなしでJSDoc風に記述できるプロポーザルが進んでおり、開発ツールによる静的解析がさらに強化されています。
まとめ
JavaScriptの変数は、単なるデータの保存場所ではなく、コードの意図を伝え、プログラムの安全性を確保するための強力なツールです。
本記事で解説したポイントを改めて整理します。
- 宣言の選択:基本は
constを使い、どうしても再代入が必要な場合のみletを使用する。varは使用しない。 - スコープの意識:ブロックスコープを理解し、変数の影響範囲を最小限に留めることで、副作用を抑える。
- データ型の特性:参照型(オブジェクト・配列)を扱う際は、
constであっても中身が変更可能であることや、参照渡しのリスクを常に意識する。 - 命名と可読性:キャメルケースなどの慣習に従い、第三者(あるいは未来の自分)が見ても意味が通じる名前を付ける。
これらの基本を徹底することで、デバッグのしやすい、高品質なJavaScriptプログラムを構築することができるようになります。
変数の正しい理解は、フロントエンドフレームワーク(React, Vue, Svelteなど)やNode.jsによるサーバーサイド開発を学ぶ上でも、揺るぎない土台となるでしょう。
一つ一つのコードに対して、「なぜこの宣言を使っているのか」という根拠を持ってプログラミングを進めていきましょう。
