ReferenceError: Cannot access 'x' before initialization

let / const / class を宣言より前で使うと出ます。var との違い、typeof でも避けられない理由、外側の同名変数を隠して起きる場合まで、原因ごとに直したコードを載せています。

実行して検証済み処理系Node 22.22.3対応ES2015難易度●●○○○目安8更新

let / const / class を、宣言より前で使ったときに出ます。

javascript
console.log(value);

let value = 1;
ReferenceError: Cannot access 'value' before initialization
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
1

直し方は単純です。宣言を、使うところより前に移します。

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
undefined
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
undefined ReferenceError

なぜ「宣言していない」ではないのか

エラー文が value is not defined ではないことに注目してください。 処理系は value の存在を知っています。 ただ、まだ使える状態になっていないだけです。

letconst は、ブロックの先頭から宣言の行までのあいだ、 一時的死角(TDZ)と呼ばれる状態に置かれます。 名前はあるのに触れない区間です。

var は違います。

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
関数宣言は先に使える
javascript
notYet();

const notYet = () => {};
ReferenceError: Cannot access 'notYet' before initialization

var は宣言が先頭に巻き上げられ、値は undefined で始まります。 だからエラーにならずに undefined が出ます。

宣言より前で使うと
var undefined気づけない
let / const エラーになる
class エラーになる
関数宣言 使える

エラーになるほうが安全です。 var は間違いを黙って通します。

typeof でも避けられない

未宣言の変数なら typeof は安全ですが、一時的死角の中では違います。

javascript
new Thing();

class Thing {}
ReferenceError: Cannot access 'Thing' before initialization
javascript
const name = '外側';

function show() {
console.log(name);

const name = '内側';
}

show();
ReferenceError: Cannot access 'name' before initialization

typeof で存在を確かめる書き方は、ここでは効きません。

原因1: 関数宣言と関数式を混同している

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
外側 内側
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
ReferenceError 1

function で書いた関数は、丸ごと巻き上げられるので先に呼べます。 ところが const に入れた関数は呼べません。

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
SyntaxError: Identifier 'dup' has already been declared

同じ「関数」でも書き方で挙動が違います。 アロー関数や関数式を使うなら、定義を呼び出しより上に置いてください。

原因2: クラスを定義より前で使う

classlet と同じ扱いです。巻き上がりません。

原因3: 外側の同じ名前を隠している

これがいちばん見つけにくい形です。

console.log(name)外側の name を見ていません。 関数の中に const name があるので、その関数の中では最初から内側の name です。 そしてその時点では、まだ触れません。

外側を使いたいなら、内側の宣言の名前を変えてください。

ブロックの中でも同じことが起きます。

似ているエラーとの違い

文言 意味
Cannot access 'x' before initialization 宣言はある。まだ使えない
x is not defined そもそも宣言が無い(綴り違い・読み込み忘れ)
Identifier 'x' has already been declared 同じ名前を2回宣言した
Assignment to constant variable. const に代入した → 別ページ

このページの実行結果は Node 22.22.3 のものです。文言は処理系と版によって変わります。自分の環境で出た文言で検索してください。

探しかたの順序

  1. エラーの文に出ている名前を控える
  2. その名前の宣言を、同じ関数・同じブロックの中で探す
  3. 見つかったら、使っている行より下にないか見る
    • 下にある → 宣言を上へ移す
    • 外側の同名を使いたかった → 内側の名前を変える
  4. 見つからなければ、varlet に変えた直後ではないか疑う

まとめ

  • 宣言はある。まだ使える状態になっていない一時的死角
  • varundefined になるだけ。エラーになるほうが安全
  • typeof でも避けられない
  • 関数宣言は巻き上がるが、const に入れた関数は巻き上がらない
  • class も巻き上がらない
  • 外側の同名を隠している場合がいちばん見つけにくい

'use strict' を書くと何が変わるかは、文字列の第1回でも同じコードを両方のモードで実行して比べています。 → 文字列の基本

この記事の根拠

  1. Let and Const Declarations — ECMAScript® 2026 Language Specificationtc39.es
  2. ResolveBinding — ECMAScript® 2026 Language Specificationtc39.es
  3. ReferenceError: can't access lexical declaration 'X' before initialization — MDNdeveloper.mozilla.org

掲載しているコード例は、公開前に読み手が押したときに動くのと同じもので実行して出力を突き合わせています。結果はリポジトリに残しています。

このページの原文(Markdown)