JavaScriptスコープ管理の核心: var/let/constの実践的比較

ES6で導入されたブロックスコープ({}内)は、変数宣言の振る舞いを根本的に変革しました。varと新規宣言子の本質的違いを実例で解説します。

スコープ特性の比較

varは関数スコープを持ち、変数の巻き上げ(hoisting)が発生します。一方let/constはブロックスコープ限定で、宣言前の参照は禁止されています。グローバルスコープでの振る舞いも異なります:

var x = 100;
console.log(window.x); // 100(windowプロパティに登録)

let y = 200;
console.log(window.y); // undefined(windowに登録されない)

クロージャ実装の差異

ループ内関数の挙動はスコープ特性を如実に示します。varを使用すると予期せぬ結果に:

const funcs = [];
for (var idx = 0; idx < 3; idx++) {
  funcs[idx] = () => console.log(idx);
}
funcs[0](); // 3(最終値が参照される)

letでは各イテレーションで独立したスコープが生成されるため、期待通りの動作:

const handlers = [];
for (let counter = 0; counter < 3; counter++) {
  handlers[counter] = () => console.log(counter);
}
handlers[1](); // 1(各counter値が保持される)

宣言子の実践的制約

1. varの特性

  • 変数巻き上げにより宣言前参照がundefined:
console.log(value); // undefined
var value = 50;
  • ブロック外へのスコープ漏れ:
for (var i = 0; i < 2; i++) {}
console.log(i); // 2(参照可能)

2. letの制約

  • ブロックスコープ外での参照エラー:
{
  let secret = "hidden";
}
console.log(secret); // ReferenceError
  • 一時的デッドゾーン(TDZ):
console.log(active); // ReferenceError
let active = true;

3. constの不変性

再代入禁止ですが、オブジェクトのプロパティ変更は許可されます:

const config = { port: 8080 };
config.port = 3000; // 有効(参照先オブジェクトの変更)
config = { host: "localhost" }; // TypeError(再代入禁止)

// 完全な不変化が必要な場合
Object.freeze(config);

スコープチェーンの深層

TDZは宣言位置までのスコープ領域で発生します。以下のコードはTDZの具体例:

let status = "initial";
if (true) {
  // statusへの参照はTDZでエラー
  status = "updated"; 
  let status = "ready";
}

ブロックスコープの理解は、非同期処理やモジュール設計において特に重要です。ループカウンタや一時変数にはlet、再代入不要な設定値にはconstを優先的に使用すべきです。

タグ: ES6 スコープ ブロックスコープ 変数宣言 クロージャ

6月19日 16:31 投稿