エラノート エラノート

JavaScript ReferenceErrorの原因と対処法

JavaScript ReferenceError エラー解決 デバッグ スコープ
広告スペース (article-top)

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は大文字と小文字を区別するため、userNameusernameは別の変数です。

よくあるスペルミスのパターン

// 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が発生します。

ブロックスコープ

letconstはブロックスコープ({}の中)で有効です。

// 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)

letconstには「一時的デッドゾーン(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がエラーにならないのは一見便利に見えますが、バグの原因になりやすいため、現在はletconstの使用が推奨されています。

関数宣言と関数式の違い

// 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について、よくある発生パターンと対処法を解説しました。

  • 変数名のスペルミスや大文字小文字の違いを確認する
  • letconstはブロックスコープで、ブロック外からはアクセスできない
  • letconstは宣言前にアクセスするとTDZによりReferenceErrorになる
  • モジュールのimport忘れやexport形式の違いに注意する
  • コールバック関数ではアロー関数を使ってthisの問題を回避する
  • typeof演算子を使うと未宣言の変数でもエラーにならずチェックできる

エラーメッセージに表示される変数名とファイルの行番号を手がかりに、「その変数がどこで宣言されているか」「スコープは正しいか」を確認するのが解決への近道です。

広告スペース (article-bottom)

あわせて読みたい