変数
データに名前をつけて保存し、プログラム中で参照 / 変更できる仕組み
変数とは
変数 (Variable) は、データに名前をつけて保存する仕組みである。プログラムの中で値を記憶し、後から参照したり変更したりできる。
宣言と代入
変数を使うには、まず宣言 (名前の登録) を行い、次に代入 (値の格納) を行う。多くの言語では宣言と代入を同時に行える。
// 宣言と代入を同時に行う
const name = "Alice";
let age = 15;
// 代入のみ (let で宣言済みの変数)
age = 16;
const と let の使い分け
TypeScript / JavaScript の宣言キーワードは const / let / var の 3 つで、実務で書くのは前の 2 つだ。
| キーワード | 再代入 | 再宣言 | スコープ | 宣言前に参照すると |
|---|---|---|---|---|
| const | 不可 | 不可 | ブロック | ReferenceError |
| let | 可 | 不可 | ブロック | ReferenceError |
| var | 可 | 可 | 関数 | undefined |
原則として const を使い、再代入が必要な場合だけ let を使う。var を避ける理由は好みではなく挙動にある。var はブロックを無視して関数全体をスコープとし、さらに宣言が関数の先頭へ巻き上げられる (ホイスティング) ため、宣言より前の行で参照してもエラーにならず undefined が返ってくる。let と const はブロックに入った時点で名前の存在自体は登録されるが、宣言に到達するまでは参照が禁止された状態 (TDZ、一時的デッドゾーン) に置かれ、宣言前に触ると ReferenceError で止まる (この扱いを巻き上げと呼ぶかは用語として揺れがある)。
console.log(v); // undefined (var は初期化前でも参照できてしまう)
var v = 1;
console.log(l); // ReferenceError: Cannot access 'l' before initialization
let l = 1;
書き間違いが undefined として静かに下流へ流れるか、その行で例外になるかの差であり、後者のほうが原因の特定が圧倒的に速い。
なお「再代入 (入っている値を入れ替える)」と「再宣言 (同じ名前をもう一度宣言する)」は別の話だ。let は再代入はできるが、同じスコープでの再宣言は構文エラーになる。var は再宣言も通るので、離れた行で同名の変数を宣言してしまっても気づけない。
型と変数
静的型付け言語では、変数に格納できるデータの種類 (型) が決まっている。
const count: number = 42; // 数値のみ
const message: string = "hello"; // 文字列のみ
const active: boolean = true; // 真偽値のみ
TypeScript は型推論が働くため、明示的な型注釈を省略しても型が決まる。ただし推論結果は宣言キーワードで変わる。let は後から別の数値を代入できるので number と広めに推論されるが、const は再代入されないことが確定しているため、値そのものを型とするリテラル型に絞り込まれる。
let total = 42; // number と推論される
const count = 42; // 42 というリテラル型に推論される
この差はユニオン型を要求する関数で表に出る。"dark" | "light" を受け取る関数には const mode = "dark" はそのまま渡せるが、let mode = "dark" は string へ広がるため型エラーになる。値を固定する意図があるなら const で宣言しておくほうが、型の面でも情報が残る。
スコープ
変数には有効範囲 (スコープ) がある。関数の中で宣言した変数は、その関数の外からは参照できない。
function greet() {
const msg = "hello"; // この関数の中だけで有効
console.log(msg);
}
// console.log(msg); // ❌ エラー: msg は関数の外では参照できない
ブロック ({}) の中で let や const で宣言した変数も、そのブロックの外からは参照できない。
命名規則
変数名はコードの読みやすさに直結する。以下の慣習が広く使われている。
| 規則 | 例 | 用途 |
|---|---|---|
| camelCase | userName | 変数、関数 |
| PascalCase | UserProfile | クラス、型 |
| UPPER_SNAKE | MAX_RETRY | 定数 |
名前は「何が入っているか」がわかるようにつける。x や tmp のような曖昧な名前は避け、userAge や itemCount のように具体的にする。
ミュータブルとイミュータブル
const の「再代入不可」は、名前がどのオブジェクトを指すかを固定するという意味に限られる。指している先の中身は自由に書き換わるため、const にしたのだから値は変わらないと思い込むと足をすくわれる。
const list = [1, 2, 3];
list.push(4); // OK: 配列の中身は変えられる
// list = [9]; // エラー: 別の配列を指し直すことはできない
const frozen = Object.freeze([1, 2, 3]);
// frozen.push(4); // TypeError: 要素を追加できない
中身まで固定したいときは Object.freeze を使う。ただしこれは最上位の 1 階層だけを凍結する浅い処理で、入れ子のオブジェクトには効かない。Object.freeze({ a: { b: 1 } }) としても a.b は書き換えられてしまう。深く固定したいなら各階層へ再帰的に適用するか、値を書き換えず新しい値を作って返す書き方に寄せる。
書き換えを禁じておくと「どこで値が変わったのか」を追う作業が消えるため、既定を const + 作り直しに置き、可変にする箇所だけを意識して選ぶのが扱いやすい。
他言語との比較
再代入を禁じる書き方は言語ごとに異なる。
| 言語 | 再代入を禁じる書き方 | 再代入できる書き方 |
|---|---|---|
| TypeScript | const | let |
| Rust | let (既定で不変) | let mut |
| Python | 構文としては用意されていない | すべての変数 |
| Java | final | 通常の宣言 |
Rust は既定が不変で、明示的に mut をつけない限り再代入できない。安全な側を何も書かずに選べる形にしてある点が、他言語との設計思想の差になる。Python には再代入を禁じる構文がないため、変えない値は MAX_RETRY のように大文字の名前にして意図を伝える慣習で運用する。
この表はいずれも再代入の可否であって、値の中身が不変かどうかは別の軸だ。Java の final も参照先の差し替えを防ぐだけで、リストへの要素追加は止められない。逆に Python の文字列やタプルは、変数の側に何も書かなくてもオブジェクト自体が不変である。
よくある間違い
次の 4 つは、書いた本人が気づきにくく、症状が出る場所も原因から離れやすい。
| 間違い | 問題 | 対策 |
|---|---|---|
| 宣言前に参照 | let / const は ReferenceError、var は undefined が流れる | 宣言と初期化をまとめて書く |
| グローバル変数の乱用 | 依存関係が不明瞭 | スコープを狭く保つ |
| 意味のない名前 | 可読性の低下 | 具体的な名前をつける |
| const なのに中身が変わる | 固定されるのは参照先だけ | 値を作り直す方針にする (Object.freeze は 1 階層のみ) |
変数の設計と命名の考え方は関連書籍に詳しい。
この記事は役に立ちましたか?
関連用語
関連する記事
foo, bar, Alice, Bob はどこから来たのか - 技術書のサンプル名の由来
技術書やコード例でおなじみの foo, bar, Alice, Bob。これらの名前にはそれぞれ歴史的な由来があります。知っていると少し楽しくなる雑学を紹介します。
コードレビューが上手い人は何を読んでいるのか
的確なコードレビューができる人は、指摘を裏づける語彙と判断基準を持っています。レビュー力を支える 3 つの読書領域と、レビューに直結する知識の身につけ方を解説します。
30 代エンジニアが読書で取り戻す「設計の言語化力」
経験年数は十分なのに設計意図を言葉にできない。30 代エンジニアが直面する「暗黙知の壁」を、技術書の読書で突破する方法を解説します。