【入門編】letとconstの使い分け:宣言的プログラミングにおける「再代入」の意図をコードで表現する – JavaScriptコア文法・モダン言語仕様と変数・関数解析バイブル

こんにちは!JavaScriptの世界へようこそ。
フロントエンドからNode.jsのバックエンドまで、日々コードを書き続けていると、「変数宣言をどうするか」という非常に小さな選択が、実はコード全体の品質やバグの少なさに直結していることに気づかされます。

他の言語、例えばPythonやJava、あるいは古いJavaScript(`var`全盛期)から入った方にとって、ES6(ES2015)で登場した `let` と `const` の使い分けは、最初は少し窮屈に感じるかもしれません。「とりあえず全部 `let` にしておけばエラーが出ないから安心……」なんて思っていませんか?

実は、`const` を積極的に使い、どうしても再代入が必要な場合のみ `let` を使うというスタイルこそが、モダンなJavaScriptにおける「宣言的プログラミング」の第一歩であり、コードの意図を未来の自分やチームメンバーに伝えるための最強のメッセージなんです。

ここをクリアすれば、あなたの書くコードの信頼性は劇的に跳ね上がりますよ。一緒に本質をマスターしていきましょう!

—

1. なぜ `var` は封印されたのか?(V8エンジンの裏側とスコープの闇)

現代のJavaScriptにおいて、`var` を使う理由はもはや1ミリもありません。なぜなら、`var` は「関数スコープ」を持ち、宣言がコードの先頭に巻き上げられる(Hoisting)という、人間にとって非常に直感に反する挙動をするからです。

// varの恐ろしい挙動(巻き上げ)
console.log(username); // エラーにならず、undefined と出力される!
var username = “Taro”;

V8エンジン(JavaScriptの実行エンジン)の視点から見ると、`var` はメモリ上の同じ領域を意図せず上書きしてしまったり、ブロック(`{}`)を無視して変数が漏れ出したりする温床でした。

この混沌とした変数スコープの問題を綺麗に解決し、変数の「寿命(ライフサイクル)」を厳格に管理するために生まれたのが、ブロックスコープを持つ `let` と `const` です。

—

2. `let` と `const` の基本:寿命と再代入のルール

まずは、それぞれの基本的な性質を整理しておきましょう。

  • `let`: 宣言したあとに「再代入(値の書き換え)」ができる変数。ただし、同じスコープ内での再宣言は不可。
  • `const`: 宣言したあとに「再代入が一切できない」定数。宣言と同時に初期化が必須。

イメージとしては、以下のような感覚です。

  • `let` は、「これから値が変わっていくカウンター」や「処理の途中で状態が上書きされる入れ物」。
  • `const` は、「一度決めたら絶対に動かさないアンカー(錨)」。

具体例で見てみましょう

// let は再代入OK
let score = 10;
score = 20; // 問題なく再代入できる
console.log(score); // 20

// const は再代入NG
const taxRate = 0.1;
// taxRate = 0.12; // ⚠️ TypeError: Assignment to constant variable. (エラーになります)

「あれ?じゃあ配列やオブジェクトを `const` で宣言したとき、中の要素を追加したり変更したりできるのはなぜ?」という疑問を持った方は、すでに鋭い着眼点を持っていますね!

JavaScriptの `const` は、「変数の箱(アドレス)の付け替えを禁止する」ものであって、「箱の中身の改変を禁止する(イミュータブルにする)」わけではありません。

const user = { name: ‘Taro’ };

// オブジェクトのプロパティを変更するのはOK(箱の中身の操作)
user.name = ‘Jiro’;
console.log(user.name); // ‘Jiro’

// 箱自体を別のオブジェクトに差し替えるのはNG
// user = { name: ‘Saburo’ }; // ⚠️ TypeError

—

3. 宣言的プログラミングにおける「再代入の意図」の表現

ここからが今回のテーマの核心です。なぜ、私たちは「できる限り `const` を使おう」と口を酸っぱくして言うのでしょうか?

それは、「この変数は、プログラムのライフサイクルの中で値が変わることはない」という強い保証(Intent)をコードの読み手に伝えるためです。

例えば、以下の2つのコードを比べてみてください。どちらが「安全で、バグを見つけやすい」でしょうか?

パターンA:すべて `let` で書かれた世界

// すべて let なので、どこで値が変わるか推論しにくい
let taxRate = 0.1;
let subtotal = 5000;
let discount = 500;
let total = 0;

// 〜 100行ほど複雑な処理が続く(中で discount や subtotal が書き換わっているかも…?)〜

total = (subtotal – discount) (1 + taxRate);

パターンB:`const` を駆使した宣言的世界

// 変わらないものはすべて const で固める
const TAX_RATE = 0.1;
const subtotal = 5000;
const discount = 500;

// 最終的な計算結果も、一度計算したら変わらないので const で宣言できる
const total = (subtotal – discount) (1 + TAX_RATE);

console.log(`お支払い金額: ${total}円`);

パターンBを見ると、`TAX_RATE` や `subtotal`、`discount`、そして `total` は、コードの実行中に一度も再代入されないことが一目で保証されています。

もし読者がコードを追っていて、「あれ?この `subtotal` の値、途中で変わってないよな?」と不安になったとき、`const` で宣言されていれば、脳内で「あ、この値はイミュータブル(不変)だな」と即座に除外して思考を進めることができます。

これが、コードの可読性を高め、認知負荷を下げるということです。プログラミングのバグの多くは、「変わるはずのない変数が、意図せずどこかで書き換わっていた」というヒューマンエラーから生まれます。`const` は、そのエラーを言語レベルで完全にシャットアウトしてくれる防壁なのです。

—

4. 実戦:モダンな配列操作における `const` の活用

実際の開発現場(例えばReactやNode.jsのAPIサーバーなど)では、変数の再代入を行うケースは驚くほど少ないです。データの加工には、`let` で変数を上書きしていくループ(`for`文など)ではなく、`map` や `filter` といった配列のメソッドを使い、常に新しい定数(`const`)を生み出す「宣言的」なスタイルが好まれます。

実用的なコード例を見てみましょう。

// ユーザーのスコア一覧
const scores = [45, 60, 80, 95, 30];

// 合格者(60点以上)だけを抽出して、さらにボーナス点を加算する処理
// 変数の再代入は一切行わず、すべて const で新しい定数として流していく
const passingScores = scores.filter(score => score >= 60);

const bonusScores = passingScores.map(score => score + 5);

console.log(‘元のスコア:’, scores); // [45, 60, 80, 95, 30]
console.log(‘ボーナス後スコア:’, bonusScores); // [65, 85, 100]

このように、データを上書きするのではなく、次々と新しい変換結果を `const` にバインドしていくことで、コードの流れがまるで上流から下流へと流れる川のようにクリアになります。

—

5. まとめ:今日から使えるベストプラクティス

ここまでの内容をギュッと凝縮した、変数宣言の黄金律(ベストプラクティス)を授けます。

1. 基本は常に `const` を使う。
2. ループのカウンタや、アルゴリズムの都合上どうしても再代入が必要な場合だけ `let` を使う。
3. `var` は歴史的遺物として心の中でそっと封印する。

「迷ったらまずは `const`」――この習慣が身についたとき、あなたの書くJavaScriptコードは、見違えるほど洗練され、バグに強いものに生まれ変わっているはずです。

ここをクリアしたあなたは、もうJavaScriptの基本文法で迷うことはありません。自信を持って、次のステップへ進んでいきましょう!

タイトルとURLをコピーしました