【入門編】【初心者向け】ブロックスコープの境界線:if文やfor文の中で宣言した変数はどこまで生きているのか – JavaScriptコア文法・モダン言語仕様と変数・関数解析バイブル

こんにちは!フロントエンドからNode.jsの深部まで、日夜JavaScriptと向き合っているシニアアーキテクトです。

プログラミングを学び始めると、必ずと言っていいほど直面するのが「変数のスコープ(有効範囲)」という壁ですよね。「さっき宣言したはずの変数なのに、なぜかエラーになる…」「気づかないうちに別の変数を上書きしてしまった…」そんな悩みを抱えたことはありませんか?

今回は、JavaScriptのモダンな変数宣言である `let` と `const` が作り出す「ブロックスコープの境界線」について、一緒に深く、そして楽しく紐解いていきましょう。ここをクリアすれば、あなたの書くコードの安全性は劇的に跳ね上がりますよ。

—

1. 昔のJavaScriptの悪夢:`var` と「関数スコープ」

ブロックスコープの話をする前に、まずはJavaScriptが歩んできた歴史(そして初学者がよくハマる罠)を少しだけ知っておきましょう。

かつて、JavaScriptで変数宣言といえば `var` しかありませんでした。`var` は「関数スコープ」というルールを持っています。これは、「関数の中であれば、どこで宣言してもその関数内ならどこからでもアクセスできる」という性質です。

百聞は一見にしかず、次のコードを見てください。

function oldWay() {
if (true) {
var hazardousVariable = “私はどこにでもいます”;
}

// if文の外なのにアクセスできてしまう!
console.log(hazardousVariable); // ➔ “私はどこにでもいます” が出力される
}

oldWay();

「えっ、`if` 文のブロックの中で宣言したのに、外側から使えちゃうの?」って思いますよね。そうなんです。`var` には、私たちが普段感覚的に捉えている「波括弧 `{}` の中だけに閉じ込める」という概念がありませんでした。これが原因で、意図しない変数の書き換えや、メモリ上の予期せぬ居残り(バグの温床)が多発していたのです。

—

2. 救世主登場:`let` と `const` がもたらす「ブロックスコープ」

そこで現代のJavaScript(ES2015以降)では、`var` の代わりに `let` と `const` を使うことが絶対のスタンダードになりました。

これらが導入したのが、今回のテーマである「ブロックスコープ」です。

ブロックスコープとは?

ブロックとは、ズバリ `{ … }` (波括弧)で囲まれた領域 のことです。`if` 文の条件分岐、`for` 文や `while` 文のループ、あるいは単なる波括弧のペアもすべて「ブロック」です。

`let` や `const` で宣言した変数は、自分が生まれたその「一番近いブロック `{}`」の壁の外には一歩も出られないという厳格な寿命(ライフサイクル)を持っています。

言葉だけだと少し硬いので、具体的なコードでその境界線を覗いてみましょう。

function modernWay() {
if (true) {
let safeLet = “私はブロックの中だけの秘密”;
const safeConst = “私もうまれません外には出ません”;

console.log(safeLet); // ➔ ブロック内なので当然OK
}

// ここは if文のブロックの外側!
// console.log(safeLet); ➔ ⚠️ ReferenceError: safeLet is not defined (エラー!)
}

modernWay();

見事にエラーになりましたね!「ブロックの外に出た瞬間、変数は消滅する(メモリから解放される準備に入る)」。これがブロックスコープの境界線です。この仕組みのおかげで、変数の寿命が最小限に抑えられ、他のコードに影響を与える心配がグッと減るんです。

—

3. 実践! `for` 文で学ぶブロックスコープの威力

ブロックスコープの恩恵を最も強く受けるのが、日々のコーディングで頻出する `for` ループ です。

昔の `var` でループを書くと、ループが終わった後もカウンター変数が外側に漏れ出してしまい、思わぬバグを引き起こすことがありました。しかし、現代の `let` を使えば、その心配は一切ありません。

function loopExample() {
for (let i = 0; i < 3; i++) { // このループの「1回ごと」に、変数 i の新しい世界(スコープ)が作られています console.log(`ループ内: i = ${i}`); } // ループの外側から i を覗こうとすると…? // console.log(i); ➔ ⚠️ ReferenceError: i is not defined } loopExample(); さらに、`let` や `const` をループ内で使うと、非同期処理(`setTimeout` やイベントリスナーなど)を扱う際によく起きる「あれ、変数の値が全部最後のループの数字になっちゃう!?」という有名なバグ(クロージャにまつわる問題)も、言語仕様レベルで綺麗に解決してくれます。 ---

4. 陥りやすい文法エラー(TDZ:一時的死区間)

最後に、ブロックスコープや巻き上げ(Hoisting)に関連して、初心者が「なんだこれ?」とパニックになりやすいポイントを一つだけ紹介しておきます。

`let` や `const` も実はコードの実行前に「巻き上げ」自体は起こっています。しかし、`var` のように勝手に `undefined` で初期化されることはありません。宣言の行に到達するまでの間、その変数は TDZ(Temporal Dead Zone:一時的死区間) という特殊な空間に置かれます。

function tdzExample() {
// 宣言の前にアクセスしようとする
console.log(mySecret); // ➔ ⚠️ ReferenceError: Cannot access ‘mySecret’ before initialization

let mySecret = “秘密のデータ”;
}

tdzExample();

「あれ?さっきは `not defined` だったのに、今度は `Cannot access … before initialization` って言われた…?」と思ったそこのあなた、素晴らしい着眼点です!

  • `not defined`: 変数がそもそも存在しない(メモリ上にない)。
  • 初期化前のアクセスエラー: 変数は存在(認識)しているけれど、まだ中身を入れるコードの行に到達していないから触らせない!

JavaScriptエンジンは、変数が宣言される前にうっかり触ってしまうミスを、このTDZという仕組みで厳しくガードしてくれているのです。変数は「使う前に、必ず上のほうで宣言・初期化する」。この基本を守っていれば、このエラーに怯える必要はまったくありませんよ。

—

まとめ:変数の寿命をコントロールしよう

今回は、`let` と `const` が作り出すブロックスコープの境界線について解説しました。

  • 関数スコープ (`var`): 関数内ならどこまでも変数が見えてしまうため、バグの温になりやすい。
  • ブロックスコープ (`let` / `const`): `{}` の外に出たら変数は消滅する。影響範囲が狭いので安全。
  • TDZ(一時적死区間): `let` / `const` は宣言前にアクセスするとガードされる。

変数の寿命をギュッと狭く、必要な場所だけに限定してあげること。これが、保守しやすくバグの少ない、美しいJavaScriptコードを書くための第一歩です。

ここをマスターしたあなたなら、もう変数まわりの挙動で迷子になることはありませんよ。自信を持って、次のコードを書きに行きましょう!

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