JavaScript | 基礎構文:スコープ・実行コンテキスト - ブロックスコープ

JavaScript JavaScript
スポンサーリンク

ブロックスコープって何者?

ブロックスコープは、
{}(ブロック)の中だけで有効な“ミニスコープ”」 のことです。

ifforwhile、単なる {} など、
波カッコで囲まれた範囲ごとに「その中だけで見える変数の部屋」ができます。

そして、letconst はこの「ブロックスコープ」に従います。
ここが、昔の var と決定的に違うポイントです。


一番シンプルなブロックスコープの例

if 文の中だけで有効な変数

if (true) {
  let x = 10;
  console.log("ブロック内:", x); // 10
}

console.log("ブロック外:", x); // エラー: x is not defined
JavaScript

xif (true) { ... }{} の中で宣言されています。
この {} が「ブロックスコープ」です。

その中では x が見えますが、
外に出ると x は存在しません。

ここが重要です。
let / const で宣言した変数は、その {} の中だけで有効」
これがブロックスコープの基本ルールです。


for 文とブロックスコープ

ループ変数はループの外に漏れない

for (let i = 0; i < 3; i++) {
  console.log("ループ内:", i);
}

console.log("ループ外:", i); // エラー: i is not defined
JavaScript

ifor 文のブロックの中だけで有効です。
ループが終わったあと、外から i を触ることはできません。

これが var だとこうなります。

for (var j = 0; j < 3; j++) {
  console.log("ループ内:", j);
}

console.log("ループ外:", j); // 3(見えてしまう)
JavaScript

var は「関数スコープ」であり「ブロックスコープを無視する」ので、
for の外からも j が見えてしまいます。

今どき var を避けるべき理由のひとつが、
この「ブロックスコープを作らない」という性質です。


ネストしたブロックスコープ

内側から外側は見える、外側から内側は見えない

let a = 1;

{
  let b = 2;
  {
    let c = 3;
    console.log(a, b, c); // 1 2 3
  }
  console.log(a, b);   // 1 2
  // console.log(c);   // エラー: c is not defined
}

console.log(a);        // 1
// console.log(b);     // エラー: b is not defined
JavaScript

スコープは「入れ子」になります。

内側のブロックからは、
外側の変数(a, b)が見えます。

でも、外側から内側の変数(cb)は見えません。

ここが重要です。
「内側から外側は見えるが、外側から内側は見えない」
これは関数スコープと同じルールで、ブロックスコープにもそのまま当てはまります。


ブロックスコープがなぜ大事なのか

一時的な変数を“外に漏らさない”ため

例えば、条件付きで何かを計算するコード。

let result;

if (Math.random() > 0.5) {
  let temp = 10;
  result = temp * 2;
} else {
  let temp = 20;
  result = temp * 3;
}

console.log(result);
// console.log(temp); // エラー: temp is not defined
JavaScript

temp は「そのブロックの中でだけ使う一時的な変数」です。
外から触る必要はありません。

ブロックスコープのおかげで、
temp は外に漏れず、安全に閉じ込められています。

もしこれが全部グローバルや関数スコープに出ていたら、
他の場所から temp をうっかり上書きしてしまうかもしれません。

ここが重要です。
ブロックスコープは「変数の寿命と見える範囲を最小限にする」ための仕組み。
スコープを狭くすることは、そのままバグのリスクを減らすことにつながります。


実務でよくある「ブロックスコープのありがたみ」

ループ内でのコールバックと let

昔よくあった罠がこれです。

for (var i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(i);
  }, 100);
}
// 3 3 3 と出る
JavaScript

var はブロックスコープを持たないので、
ループが終わった時点で i は 3。
その同じ i を3回参照してしまいます。

let を使うと、
各ループごとに「そのループ専用の i」がブロックスコープで作られます。

for (let i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(i);
  }, 100);
}
// 0 1 2 と出る
JavaScript

ここが重要です。
let が「ループごとに別々の i を持てる」のは、
ブロックスコープのおかげ。
非同期処理と組み合わせたときに、バグを防いでくれます。


単なる {} でもブロックスコープを作れる

意図的に“囲って”スコープを分ける

iffor でなくても、
単に {} で囲むだけでブロックスコープを作れます。

{
  const message = "ここだけのメッセージ";
  console.log(message); // OK
}

console.log(message); // エラー: message is not defined
JavaScript

「この変数はこの数行でしか使わない」
というときに、あえてブロックで囲んでスコープを狭める、
という書き方もできます。

モジュールや関数が前提の今どきコードでは出番は少なめですが、
{} がスコープを作る」という感覚をつかむには良い練習です。


初心者として「ブロックスコープ」で本当に押さえてほしいこと

ブロックスコープは、
{} の中だけで有効なスコープ」 であり、
let / const はこのルールに従う、ということ。

if、for、while、単なる {} など、
どのブロックでも同じようにスコープができる。

内側から外側は見えるけど、
外側から内側は見えない。

これを意識すると、

一時的な変数を外に漏らさない
ループ変数が外に残らない
同じ名前の変数を別々のブロックで安心して使える

といったメリットが得られます。

コードを書くとき、
「この変数、本当はこのブロックの中だけでよくない?」
と一度問いかけてみてください。

その瞬間から、
あなたは「なんとなく変数を置く人」ではなく、
「スコープを設計しているプログラマー」 になっていきます。

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