JavaScript | ES6+ 文法:変数・宣言の進化 – ブロックスコープ

JavaScript
スポンサーリンク

ブロックスコープとは何か

ブロックスコープは「波括弧 { } で囲まれた“その範囲内だけ”有効な変数の生存範囲」です。ES6 以降、let と const はブロックスコープを持ち、宣言されたブロックの外からは見えません。ここが重要です:意図せず外側の変数を書き換えたり、値が漏れたりする事故を防げます。

{
  const message = "hello";
  console.log(message); // "hello"
}
console.log(typeof message); // "undefined"(ブロック外では見えない)
JavaScript

var と let/const の違い(関数スコープ vs ブロックスコープ)

var は“関数スコープ”で、ブロックをまたいでも見えてしまいます。let/const は“ブロックスコープ”で、if/for/switch/try などの内側に閉じます。ここが重要です:実務では let/const を標準にし、var は使わない方針が安全です。

if (true) {
  var a = 1;
  let b = 2;
}
console.log(a); // 1(var はブロック外でも見える)
console.log(typeof b); // "undefined"(let はブロック外から見えない)
JavaScript

Temporal Dead Zone(宣言前は使えない)

ブロックスコープ内では、let/const は“宣言より前の行”で参照できません。これが Temporal Dead Zone(TDZ)です。ここが重要です:未初期化の値をうっかり使うバグを、その場で止めます。

{
  // console.log(x); // ReferenceError(TDZ)
  let x = 10;
  console.log(x); // 10
}
JavaScript

同名変数を内側ブロックで再宣言する場合も、宣言前の行は TDZ になります。

let n = 5;
{
  // console.log(n); // ReferenceError(このブロックで n を宣言予定のため TDZ)
  let n = 10;
  console.log(n); // 10(内側の n)
}
console.log(n); // 5(外側の n)
JavaScript

シャドーイングとスコープチェーン(内側が外側を“隠す”)

内側のブロックで同名の let/const を宣言すると、外側の同名変数は“見えなく”なります(シャドーイング)。ここが重要です:内側で安全に一時的な上書きができますが、紛らわしい名前は避けるのがコツです。

const label = "outer";
{
  const label = "inner"; // 外側を隠す
  console.log(label); // "inner"
}
console.log(label); // "outer"
JavaScript

スコープチェーンは「内側から外側へ順に探す」仕組みです。見つからなければさらに外へ、最後にグローバルへ辿ります。ブロックスコープを使うと“探す範囲”が自然に小さくなり、意図が伝わります。


ループと非同期での恩恵(クロージャが期待どおりに動く)

for で let を使うと「反復ごとに新しい束縛」が作られ、非同期でもインデックスが期待通りに閉じ込められます。ここが重要です:ES5 時代の var で起きがちな“全部同じ値”問題を解消します。

for (let i = 1; i <= 3; i++) {
  setTimeout(() => console.log(i), 0); // 1, 2, 3
}

for (var j = 1; j <= 3; j++) {
  setTimeout(() => console.log(j), 0); // 4, 4, 4(var は最後の値を共有しがち)
}
JavaScript

for-of/for-in で const を使うと、各反復で“変えない値”を安全に扱えます。カウンタや累積など“変える値”は let を使い分けます。

let sum = 0;
for (const n of [1,2,3]) sum += n; // n は反復ごとに新束縛、sum は let で更新
JavaScript

switch と try-catch の注意点(単一ブロックを分割する)

switch 文は“全体が1つのブロック”なので、複数の case で同名 let/const を宣言すると再宣言エラーになります。ここが重要です:case ごとに { } を置いてブロックを分けます。

switch (kind) {
  case "A": {
    const x = 1;
    console.log(x);
    break;
  }
  case "B": {
    const x = 2; // 別ブロックなので OK
    console.log(x);
    break;
  }
}
JavaScript

try-catch では、catch (e) の e は catch ブロックにだけ見えます。外へ漏らしたくない一時変数を安全に閉じ込めるのに向きます。

try {
  throw new Error("oops");
} catch (e) {
  const msg = e.message; // ここだけ
}
// console.log(msg); // ReferenceError(ブロック外)
JavaScript

実務での使い分け(指針と例)

実務では“まず const、必要なら let”が基本です。宣言は使う直前に置き、ブロックを活用してスコープを最小化します。ここが重要です:スコープが小さいほど、意図せぬ上書き・未初期化の事故が減ります。

function compute(items) {
  const rate = 0.1;                 // 不変値は const
  let total = 0;                    // 累積は let
  for (const it of items) {
    const price = it.price ?? 0;    // 反復内の一時値は const
    total += Math.round(price * (1 + rate));
  }
  return total;
}
JavaScript

ブロックスコープは“ミニ関数”のように使えます。一時的な名前を外へ漏らさず、意図のまとまりをつくれます。

{
  const key = query.trim().toLowerCase();
  result = items.filter(x => x.name.toLowerCase().includes(key));
}
// ここでは key を参照できない(安全)
JavaScript

まとめ

ブロックスコープの核心は「変数の寿命を“必要な範囲”に閉じ込める」ことです。let/const は TDZ により宣言前使用を防ぎ、内側でのシャドーイングで外側を守ります。ループと非同期では反復ごとに新束縛が作られ、インデックスが期待通りに動きます。switch は case をブロック化、try-catch は一時変数を安全に閉じる。“まず const、必要なら let、宣言は使う直前、スコープは最小化”を徹底すれば、初心者でもバグを避けながら読みやすい ES6+ のコードを書けます。

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