再代入不可とは何か
「再代入不可」は、同じ変数名に“別の値(参照)をもう一度割り当てることができない”という意味です。ES6 の const はブロックスコープで、宣言した後にその変数へ新しい値を入れ直すとエラーになります。ここが重要です:再代入不可は“参照が固定される”ことであり、“中身が不変”とは限りません(特にオブジェクトや配列)。不変にしたいのは値なのか参照なのかを、意識的に区別します。
const x = 1;
// x = 2; // TypeError: 再代入不可
JavaScript参照の固定と値の変化(プリミティブ vs オブジェクト)
プリミティブは完全に固定される
数値・文字列・真偽値などのプリミティブは“値そのもの”が変わらないため、const にすると完全固定です。代入しか変更手段がないので、当然再代入不可の制約と相性が良いです。
const label = "hello";
// label = "world"; // NG(再代入不可)
JavaScriptオブジェクトは“参照だけ”固定される
const でオブジェクトや配列を宣言すると、“どのオブジェクトを指すか”は固定されますが、プロパティや要素は変更できます。ここが重要です:参照固定(const)と中身変更(ミューテーション)は別物。容器は固定、中身は変化という使い方が実務ではよくあります。
const user = { name: "Alice" };
user.name = "Bob"; // OK(中身変更)
/* user = { name: "Carol" }; */ // NG(別参照への再代入)
const list = [1, 2];
list.push(3); // OK(中身変更)
/* list = []; */ // NG(別参照へ)
JavaScript本当に不変にしたいとき(凍結と深い不変)
Object.freeze で“浅い不変”
Object.freeze は“そのオブジェクトの直下プロパティ”を凍結します。ここが重要です:浅い凍結なので、ネストの内側は変更できてしまいます。不変の保証範囲を理解して使います。
const cfg = Object.freeze({ a: 1, nested: { b: 2 } });
// cfg.a = 9; // 無効(厳格モードなら例外)
cfg.nested.b = 3; // 変更できてしまう(浅い凍結)
JavaScript深い不変が必要なら再帰的に凍結
深い不変を求めるときは、再帰的に freeze するユーティリティを用意します。大きな構造ではコストが高いので、用途を絞るのが現実的です。
function deepFreeze(obj) {
if (obj && typeof obj === "object") {
Object.getOwnPropertyNames(obj).forEach(k => deepFreeze(obj[k]));
Object.freeze(obj);
}
return obj;
}
const cfg = deepFreeze({ a: 1, nested: { b: 2 } });
JavaScript宣言と初期化(TDZ と宣言位置の意味)
const は必ず初期化が必要
const は宣言と同時に初期化しなければなりません。ここが重要です:宣言前は TDZ(Temporal Dead Zone)にあり、参照できません。未初期化のまま使うバグを“その場で止める”安全機構です。
/* const x; */ // SyntaxError(初期化必須)
console.log(n); // ReferenceError(TDZ)
const n = 5;
JavaScript使う直前で宣言する
読みやすさと安全性のため、“必要な場所で宣言・初期化”します。広いスコープに余計な束縛を作らないのがコツです。
function totalPrice(price) {
const rate = 0.1; // 不変値
const taxed = Math.round(price * (1 + rate));
return taxed;
}
JavaScript実務の使い分け(const を基準、必要なら let)
原則 const、変える必要があるなら let
不変を増やすほど意図が明確になり、バグが減ります。ここが重要です:まず const を使い、カウンタや累積のように“値を更新する必要がある”と判明したときだけ let に切り替えます。
const items = [1, 2, 3]; // 参照は固定
let sum = 0; // 累積は可変
for (const n of items) sum += n;
JavaScript“容器は固定、中身は更新”のパターン
配列やオブジェクトの“入れ物”は const で、要素追加やプロパティ更新は内部で行います。参照のライフサイクルが安定し、差分検知(===)やキャッシュと相性が良くなります。
const buffer = [];
buffer.push({ id: 1 });
buffer.push({ id: 2 }); // 容器は同じ参照を保つ
JavaScriptよくある落とし穴と対策(重要ポイントの深掘り)
“再代入不可=中身も不変”の誤解
const は参照を固定するだけで、中身が不変になるわけではありません。中身まで不変にしたいなら freeze(浅い)や深い凍結(重い)を使う、あるいは非破壊更新(新インスタンスを作る)に切り替えます。
const state = { user: { name: "A" } };
// 非破壊更新(新インスタンス)
const next = { ...state, user: { ...state.user, name: "B" } };
JavaScriptswitch の再宣言エラー
switch は全体が単一ブロックなので、case ごとに同名 const を宣言するとエラーになります。各 case を { } で囲んでブロックを分けます。
switch (type) {
case "A": { const msg = "alpha"; break; }
case "B": { const msg = "beta"; break; }
}
JavaScript破壊的メソッドと const の相性
const でも配列の push / sort(破壊的)などは実行できますが、共有状態では副作用になります。ここが重要です:UIや共有データは“非破壊”を原則にし、toSorted などの非破壊版を使うと安全です。
const rows = [3,1,2];
const sorted = rows.toSorted((a,b)=>a-b); // rows は保つ
JavaScript例題で理解を固める
期待通りの安全性(参照固定でうっかり上書きを防ぐ)
function buildQuery(params) {
const base = "https://api.example.com";
const q = `?page=${params.page}&per=${params.per}`;
return base + q;
// base を間違って上書きするバグを const が防ぐ
}
JavaScript非破壊設計と const の組み合わせ
const users = [{ id: 1, active: false }, { id: 2, active: true }];
// 状態は非破壊更新+参照固定で管理
const nextUsers = users.map(u => u.id === 1 ? { ...u, active: true } : u);
// users はそのまま、nextUsers は新参照
JavaScriptまとめ
再代入不可の核心は「変数が指す参照を固定する」ことです。プリミティブは完全不変、オブジェクト/配列は“中身の変更は可能”という現実を理解するのが重要です。完全不変が必要なら凍結を使い、普段は“容器は const、更新は非破壊”で安全に運用する。宣言は使う直前、原則 const・必要なら let、switch はブロック化。これを徹底すれば、初心者でも意図が伝わり、バグを防ぎながら安定したコードを書けます。
