- JavaScript
Cannot access before initializationの直し方
ReferenceError: Cannot access 'X' before initialization は、let・const・class で宣言した名前を、宣言の行が実行される前に使ったときのエラーです。 宣言より前の、使えない区間を「一時的デッドゾーン(TDZ)」と呼びます。
原因は大きく2つです。
| 原因 | よくある形 | 直し方 |
|---|---|---|
| 同じファイルの中で、宣言より前に使った | console.log(x); let x = 1;、関数を宣言前に呼んだ |
宣言を使う場所より前に移す |
| ファイル同士が互いに import している(循環 import) | index.js でまとめた export、ビルド後に 'd' のような1文字の名前で出る |
循環を切る(直接 import する・使うのを関数の中にする) |
確認環境:Node.js 22.22.2、Chromium 141、Vite 6.4.4(確認日 2026年10月11日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Cannot access before initializationの直し方」
メッセージはブラウザで違う
| 実行環境 | メッセージ |
|---|---|
| Chrome・Edge(Chromium)、Node.js | Cannot access 'X' before initialization(確認環境で同じ文言) |
| Firefox | can't access lexical declaration 'X' before initialization(MDN による) |
| Safari | Cannot access uninitialized variable.(MDN による) |
Safari では、変数の名前が表示されません。 スタックトレースの行番号から場所を探します。
同じファイルの中で起きる例
確認環境(Node.js)で、次の形を試しました。
| コード | 結果 |
|---|---|
console.log(x); let x = 1; |
ReferenceError |
console.log(y); const y = 1; |
ReferenceError |
new Foo(); class Foo {} |
ReferenceError(class も同じ) |
g(); const g = () => 1; |
ReferenceError(アロー関数を const に入れた) |
console.log(typeof q); let q = 1; |
ReferenceError(typeof でもエラー) |
let a = 1; { console.log(a); let a = 2; } |
ReferenceError(ブロックの中の a が外の a を隠す) |
console.log(z); var z = 1; |
undefined(var はエラーにならない) |
h(); function h() {} |
動く(function 宣言は前から呼べる) |
console.log(nope);(宣言なし) |
nope is not defined(別のエラー) |
function h() {} の形は宣言より前に呼べますが、const g = () => {} の形は呼べません。 関数を const に書き換えたら、このエラーが出るようになった、というのはこのためです。
is not defined は、その名前がどこにも宣言されていないエラーです。 before initialization は「宣言はあるが、まだその行が実行されていない」なので、見るところが違います。
「書いた位置」ではなく「実行した順番」
| コード | 結果 |
|---|---|
function f() { return c; } f(); let c = 1; |
ReferenceError(f() を呼んだ時点で、c の行はまだ) |
function f() { return c; } let c = 1; f(); |
動く(1) |
関数の中で使うだけなら、宣言より前に書いてあっても問題ありません。呼んだ時点で、宣言の行が実行済みかどうかで決まります(MDN の let の解説も同じ)。React のコンポーネントや、addEventListener に渡す関数で、あとから呼ばれる場合は動くのに、初期化の途中で呼ぶとエラーになるのはこのためです。
直し方:宣言を、使う場所より前に移します。 var に戻すとエラーは消えますが、undefined のまま進んでしまうので、別の不具合になります。
ファイル同士の循環 import で起きる例
// a.mjs
import { b } from './b.mjs';
export const A = 'A';
// b.mjs
import { A } from './a.mjs';
export const b = 'b';
console.log('b sees', A); // ファイルの一番上の階層で、A を使う
(a.mjs を読み込む)
file:///.../b.mjs:3
console.log('b sees', A);
^
ReferenceError: Cannot access 'A' before initialization
a.mjs が読み込みを始める → 1行目で b.mjs へ → b.mjs が A を使う → a.mjs はまだ1行目の途中なので、A が初期化されていない、という順番です。
よくある形:index.js でまとめた export
// components/index.js(まとめて export する)
export { Card } from './Card.js';
export { Button } from './Button.js';
// components/Card.js
import { Button } from './index.js'; // ← 自分を export している index.js から import
export const CardButton = Button('OK'); // ← 読み込み時に Button を使う
export const Card = () => `Card ${CardButton}`;
ReferenceError: Cannot access 'Button' before initialization
index.js が Card を先に読み込み、Card.js は index.js から Button を取ろうとするが、index.js はまだ Button の行まで進んでいないため、エラーになりました。index.js の export の順番を入れ替えると動いたので、ファイルを1つ足しただけで突然出る、ということが起きます。
Vite・ビルド後は「'd'」のような1文字の名前になる
同じコードを Vite でビルドし、Chromium で開くと、名前が1文字になりました。
| 実行のしかた | エラー |
|---|---|
| Vite の開発サーバー | Cannot access 'Button' before initialization(Card.js:2:27) |
vite build(圧縮あり) |
Cannot access 'd' before initialization |
vite build --minify false |
圧縮しない。出力で Button を探すと、使う行が定義より前にあると分かる |
// vite build --minify false の出力(抜粋)
const CardButton = Button("OK"); // 使う
const Card = () => `Card ${CardButton}`;
const Button = (label) => `[${label}]`; // 定義はあと
ビルドすると全部のファイルが1つにまとまり、循環 import が「同じファイルの中で、宣言より前に使った」形に変わります。 本番だけで出る 'd' のようなエラーは、--minify false でビルドし直すか、ソースマップで元の名前を確かめます。
循環 import の直し方
| 直し方 | 書き方 |
|---|---|
| index.js を経由せず、直接 import | import { Button } from './Button.js'(同じフォルダーの中では index.js を使わない) |
| 使うのを関数の中にする | export const Card = () => \Card ${Button('OK')}`;`(呼ばれたときに使う) |
| 共通の部分を別のファイルに分ける | 互いに使う部分を、どちらも import しないファイルに移す |
確認環境では、Card.js の import を ./Button.js に変える、またはButton を使うのを関数の中に移すと、どちらも Card [OK] と動きました。
読み込みの時点(ファイルの一番上の階層)で、import した値を使っていないかを見ます。関数の中で使うだけなら、呼ばれるころには読み込みが終わっています。
確認の手順(チェックリスト)
| 順番 | 確認すること | 方法 |
|---|---|---|
| 1 | エラーの名前と行 | Cannot access 'X' の X と、スタックトレースの1行目 |
| 2 | 同じファイルに X の宣言があるか | あれば、宣言を使う場所より前に移す |
| 3 | X が import したものか | 循環 import を疑う |
| 4 | index.js 経由の import か | 同じフォルダーの中では直接 import に |
| 5 | 読み込み時に使っていないか | ファイルの一番上の階層での呼び出しを、関数の中へ |
| 6 | 1文字の名前(ビルド後) | vite build --minify false かソースマップで元の名前を確かめる |
よくある質問
Q. let を var に変えれば直りますか。
A. エラーは出なくなりますが、値は undefined のまま使われ、別の場所で Cannot read properties of undefined などになります。宣言の順番か、循環 import を直します。
Q. 循環 import を見つける方法はありますか。
A. スタックトレースに出ているファイルの import をたどると、輪になっている箇所が分かります。プロジェクト全体では、ESLint の eslint-plugin-import の import/no-cycle ルールが使えます。上の Card.js と index.js の例では、確認環境(ESLint 9.39)で Dependency cycle detected import/no-cycle と、両方のファイルが検出されました。
まとめ
let・const・classを、宣言の行が実行される前に使ったエラーvarはundefined、function宣言は前から呼べるので、書き換えたときに出やすい- 書いた位置ではなく、実行した順番で決まる
- 循環 import でも出る。index.js でまとめた export の順番で、突然出ることがある
- ビルド後は
'd'のような1文字の名前になる。--minify falseで元の名前を確かめる - 直し方は宣言を前へ・直接 import・使うのを関数の中へ
参考・出典
確認日はいずれも 2026年10月11日です。
- MDN「ReferenceError: can't access lexical declaration 'X' before initialization」:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_access_lexical_declaration_before_init
- MDN「let」(Temporal dead zone (TDZ)):https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Cannot access before initializationの直し方」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。