JavaScript | 基礎構文:スコープ・実行コンテキスト – TDZ(Temporal Dead Zone)

JavaScript JavaScript
スポンサーリンク

TDZ(Temporal Dead Zone)とは何か

TDZ は「一時的な死角」という意味で、letconstclass などが宣言されてから初期化されるまでのあいだ、その識別子にアクセスするとエラーになる領域を指します。ポイントは「変数は“巻き上げ(ホイスティング)”されるが、初期化されるまでは使えない」ということです。これにより、未定義の値をうっかり読むミスを防ぎ、コードの安全性が高まります。


どうして起こるのか(ホイスティングとの違いを深掘り)

var はホイスティングによって宣言と初期化前でも参照でき、値は undefined になります。一方、let/const は宣言自体はスコープに登録されるものの、初期化が実行されるその瞬間まで「TDZ」にあり、参照しようとすると ReferenceError が発生します。重要なのは「スコープに存在していても、初期化前は触れられない」という設計で、早期にバグを露見させます。


例題1:let/const の TDZ を体感する

console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 10;

console.log(b); // ReferenceError
const b = 20;
JavaScript

このコードでは、ab はスコープに登録済みですが、初期化が行われる行より前では TDZ にあるため、参照できません。var と異なり undefined は返りません。


例題2:ブロックスコープ内の TDZ

{
  // TDZの範囲:この行から下、初期化行まで
  // ここで x に触れると ReferenceError
  // console.log(x); // ReferenceError

  let x = 1; // ここで初期化が完了
  console.log(x); // 1
}
JavaScript

let はブロック {} の開始時点でスコープに登録されますが、初期化されるまでのブロック内は TDZ です。初期化後は通常通り利用できます。


例題3:関数のデフォルト引数にも TDZ がある(深掘り)

let rate = 0.1;
function calc(price, rate = rate) { // ここで内側の rate を参照してしまう
  return Math.floor(price * (1 + rate));
}
JavaScript

このコードは紛らわしく、実行時に ReferenceError になります。理由は、引数リスト内では「内部の rate」がすでにスコープに登録されており、まだ初期化されていない自分自身を参照しているため TDZ に触れてしまうからです。安全に書くなら次のように外側を明示して回避します。

const defaultRate = 0.1;
function calc(price, rate = defaultRate) {
  return Math.floor(price * (1 + rate));
}
JavaScript

例題4:class 宣言も TDZ の対象

const inst = new Person(); // ReferenceError: Cannot access 'Person' before initialization
class Person {
  constructor() {}
}
JavaScript

classlet/const と同様に TDZ に入ります。宣言より前に new でインスタンス化しようとすると ReferenceError になります。クラスは関数宣言のように完全ホイスティングされない点を覚えておくと混乱を避けられます。


var との比較で理解を固める(重要ポイントのコントラスト)

console.log(v); // undefined(var は参照できる)
var v = 5;

console.log(l); // ReferenceError(let はTDZ)
let l = 5;
JavaScript

var は初期化前でも undefined を返しますが、これは「未初期化の読み取りを許す」ため、意図せず undefined のまま計算に混ざるバグを招きます。let/const は TDZ によって「未初期化のアクセスを明確にエラーにする」ため、問題が早期に顕在化します。


よくある落とし穴と回避方法(深掘り)

同名の外側変数を引数のデフォルトで参照する

引数名が外側の変数名と同じだと、内側の識別子が先にスコープ登録され、外側よりも優先されるため自分自身を参照して TDZ になります。引数のデフォルトには別名の定数を用意するか、オブジェクトにまとめて渡すなどで衝突を避けます。

宣言より前で使うロジック配置

「上にログ、下に宣言」の並びは TDZ に当たります。宣言と初期化は、使用箇所より先に書くのが基本です。モジュールや関数の先頭で必要な変数を初期化してから使うように順序を整えましょう。


TDZ の意義と設計指針

TDZ は「未初期化の利用」を明確にエラーにすることで、バグを早期に発見できる仕組みです。次の感覚を持つと安全に書けます。宣言と初期化は使用の前に置く、let/const/class は宣言より前では触れない、デフォルト引数で同名衝突を避ける。結果としてコードの意図がはっきりし、読みやすさと安全性が両立します。


まとめ

TDZ は letconstclass などが「スコープに登録済みでも初期化前は触れられない」期間を指し、参照すると ReferenceError になります。var のような暗黙の undefined を避け、未初期化アクセスをエラーにすることで、意図しない挙動を防ぎます。宣言の順序を守り、デフォルト引数やクラス宣言の TDZ を意識すれば、より予測可能で安全な JavaScript を書けるようになります。

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