ブロックスコープとは何か
ブロックスコープは「波括弧 { } で囲まれた“その範囲内だけ”有効な変数の生存範囲」です。ES6 以降、let と const はブロックスコープを持ち、宣言されたブロックの外からは見えません。ここが重要です:意図せず外側の変数を書き換えたり、値が漏れたりする事故を防げます。
{
const message = "hello";
console.log(message); // "hello"
}
console.log(typeof message); // "undefined"(ブロック外では見えない)
JavaScriptvar と 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 はブロック外から見えない)
JavaScriptTemporal 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 は最後の値を共有しがち)
}
JavaScriptfor-of/for-in で const を使うと、各反復で“変えない値”を安全に扱えます。カウンタや累積など“変える値”は let を使い分けます。
let sum = 0;
for (const n of [1,2,3]) sum += n; // n は反復ごとに新束縛、sum は let で更新
JavaScriptswitch と 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;
}
}
JavaScripttry-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+ のコードを書けます。
