JavaScript ReferenceErrorの原因と対処法
JavaScriptのReferenceErrorは、存在しない変数を参照しようとしたときに発生するエラーです。変数名のスペルミスやスコープの問題、letやconstの巻き上げ(TDZ)など、初心者がつまずきやすいポイントが多く含まれています。この記事では、ReferenceErrorのよくある発生パターンと対処法をエラーメッセージ別に解説します。
ReferenceErrorとは
ReferenceErrorは、宣言されていない変数や、まだアクセスできない変数を参照したときに発生する例外です。
エラーメッセージの基本形
console.log(myVariable);
// ReferenceError: myVariable is not defined
「myVariableは定義されていません」という意味です。JavaScriptエンジンが変数myVariableを現在のスコープ内で見つけられなかった場合に発生します。
“is not defined” と “undefined” の違い
この2つは全く別の意味です。混同しやすいので注意してください。
// ReferenceError: 変数そのものが存在しない
console.log(notDeclared);
// ReferenceError: notDeclared is not defined
// undefined: 変数は存在するが値が代入されていない
let declared;
console.log(declared); // undefined(エラーにはならない)
パターン1: 変数名のスペルミス
最も単純で頻出するパターンです。
スペルミスの例
// NG: 変数名を間違えている
const userName = "田中";
console.log(username);
// ReferenceError: username is not defined
// OK: 正しい変数名を使う
const userName = "田中";
console.log(userName); // 田中
JavaScriptは大文字と小文字を区別するため、userNameとusernameは別の変数です。
よくあるスペルミスのパターン
// NG: 大文字小文字の間違い
document.getElementById("app"); // OK
document.getElementByid("app"); // NG: Idが小文字になっている
// NG: 関数名のスペルミス
console.log(parceInt("42"));
// ReferenceError: parceInt is not defined
// OK: 正しくはparseInt
console.log(parseInt("42")); // 42
対処法
変数名や関数名が正しいかを確認するには、次の方法が役立ちます。
- エディタの補完機能(VSCodeなど)を活用する
- ブラウザの開発者ツールのConsoleでエラーメッセージを確認する
typeof演算子で変数の存在を安全に確認する
// typeofは未宣言の変数でもエラーにならない
if (typeof myVar !== "undefined") {
console.log(myVar);
}
パターン2: スコープの問題
変数が宣言されたスコープの外からアクセスしようとするとReferenceErrorが発生します。
ブロックスコープ
letとconstはブロックスコープ({}の中)で有効です。
// NG: ブロックの外からアクセスできない
if (true) {
let message = "こんにちは";
const count = 10;
}
console.log(message);
// ReferenceError: message is not defined
// OK: ブロックの外で宣言する
let message;
if (true) {
message = "こんにちは";
}
console.log(message); // こんにちは
関数スコープ
関数内で宣言した変数は、関数の外からはアクセスできません。
// NG: 関数内の変数は外からアクセスできない
function greet() {
const greeting = "こんにちは";
return greeting;
}
console.log(greeting);
// ReferenceError: greeting is not defined
// OK: 関数の戻り値として受け取る
function greet() {
const greeting = "こんにちは";
return greeting;
}
const result = greet();
console.log(result); // こんにちは
forループ内の変数
// NG: letで宣言したループ変数はループ外からアクセスできない
for (let i = 0; i < 5; i++) {
// iはこのブロック内でのみ有効
}
console.log(i);
// ReferenceError: i is not defined
// OK: ループ外で使いたい場合は外で宣言する
let lastIndex;
for (let i = 0; i < 5; i++) {
lastIndex = i;
}
console.log(lastIndex); // 4
パターン3: 一時的デッドゾーン(TDZ)
letとconstには「一時的デッドゾーン(Temporal Dead Zone、TDZ)」という仕組みがあり、宣言より前にアクセスするとReferenceErrorになります。
TDZの例
// NG: 宣言より前にアクセス(TDZ)
console.log(name);
// ReferenceError: Cannot access 'name' before initialization
let name = "田中";
// OK: 宣言の後にアクセスする
let name = "田中";
console.log(name); // 田中
varとの違い
varは巻き上げ(hoisting)により、宣言前にアクセスしてもundefinedになるだけでエラーにはなりません。
// varは巻き上げられる(エラーにはならない)
console.log(x); // undefined
var x = 10;
// letはTDZによりエラーになる
console.log(y);
// ReferenceError: Cannot access 'y' before initialization
let y = 10;
varがエラーにならないのは一見便利に見えますが、バグの原因になりやすいため、現在はletとconstの使用が推奨されています。
関数宣言と関数式の違い
// OK: 関数宣言は巻き上げられるので宣言前に呼べる
greet(); // "こんにちは"
function greet() {
console.log("こんにちは");
}
// NG: 関数式(const)は巻き上げられない
hello();
// ReferenceError: Cannot access 'hello' before initialization
const hello = function() {
console.log("Hello");
};
// NG: アロー関数も同様
sayHi();
// ReferenceError: Cannot access 'sayHi' before initialization
const sayHi = () => {
console.log("Hi");
};
パターン4: モジュールのインポート忘れ
別ファイルで定義された関数やクラスをインポートせずに使おうとするとReferenceErrorになります。
importの書き忘れ
// NG: インポートせずに使用
const result = calculateTotal(items);
// ReferenceError: calculateTotal is not defined
// OK: 正しくインポートする
import { calculateTotal } from "./utils.js";
const result = calculateTotal(items);
default exportとnamed exportの混同
// utils.js で export default した場合
// NG: 波括弧を使うとインポートできない
import { myFunction } from "./utils.js";
// myFunctionはundefinedになりうる
// OK: default exportは波括弧なしでインポート
import myFunction from "./utils.js";
パターン5: thisの参照問題
コールバック関数やイベントハンドラ内でthisが期待と異なるオブジェクトを指すことがあります。
コールバック内のthis
class Timer {
constructor() {
this.seconds = 0;
}
start() {
// NG: setInterval内のthisがTimerインスタンスを指さない
setInterval(function() {
this.seconds++;
// TypeError: Cannot read properties of undefined (reading 'seconds')
}, 1000);
}
}
class Timer {
constructor() {
this.seconds = 0;
}
start() {
// OK: アロー関数を使うとthisが保持される
setInterval(() => {
this.seconds++;
console.log(this.seconds);
}, 1000);
}
}
アロー関数は自身のthisを持たず、外側のスコープのthisをそのまま使います。コールバック関数では積極的にアロー関数を使うのがおすすめです。
デバッグのコツ
ReferenceErrorに遭遇したときに素早く原因を特定する方法をまとめます。
エラーメッセージを正確に読む
ReferenceError: myVar is not defined → 変数が未宣言
ReferenceError: Cannot access 'x' before initialization → TDZ(宣言前アクセス)
メッセージの末尾に表示されるファイル名と行番号も確認しましょう。
typeof演算子で安全に確認
// 変数が存在するか安全に確認する方法
if (typeof someVar !== "undefined") {
// someVarが存在する場合のみ実行
console.log(someVar);
}
try/catchで例外を捕捉
try {
console.log(unknownVar);
} catch (error) {
if (error instanceof ReferenceError) {
console.error("変数が見つかりません:", error.message);
}
}
まとめ
JavaScriptのReferenceErrorについて、よくある発生パターンと対処法を解説しました。
- 変数名のスペルミスや大文字小文字の違いを確認する
letとconstはブロックスコープで、ブロック外からはアクセスできないletとconstは宣言前にアクセスするとTDZによりReferenceErrorになる- モジュールのimport忘れやexport形式の違いに注意する
- コールバック関数ではアロー関数を使って
thisの問題を回避する typeof演算子を使うと未宣言の変数でもエラーにならずチェックできる
エラーメッセージに表示される変数名とファイルの行番号を手がかりに、「その変数がどこで宣言されているか」「スコープは正しいか」を確認するのが解決への近道です。