コハム

Webクリエイターのコハムが提供する、Web制作に役立つ情報をまとめたブログです。

【永久保存版】JavaScript初心者が陥る10大エラーを徹底解説!〜明日から使える現場のデバッグテクニック大公開〜

Top 10 JavaScript Errors and How to Debug Them

記事は上記記事を意訳したものです。

※当ブログでの翻訳記事は元サイト様に許可を得て掲載しています。


JavaScriptは、単純なウェブサイトのインタラクションから複雑なウェブアプリケーションまで、世界で最も広く使用されているプログラミング言語の1つです。しかし、JavaScriptは強力である一方で、コードの実行を停止させる可能性のあるエラーを発生させることでも有名です。JavaScriptのエラーのデバッグは、特に意味不明な暗号のようなメッセージに直面したとき、非常に困難な作業となることがあります。しかし、朗報があります。ほとんどのエラーは一般的なものであり、原因を理解すれば簡単な解決策があります。

この記事では、開発者が遭遇する上位10個のJavaScriptエラーを解説し、デバッグと修正のための詳細な実用的な手順を提供します。JavaScriptの初心者でもベテラン開発者でも、これらの一般的なエラーとその解決方法を理解することで、コーディングの過程で時間と労力を節約することができます。

1. Uncaught TypeError: Cannot Read Property 'X' of Undefined

JavaScript開発者が最も頻繁に遭遇するエラーの1つが、この有名なTypeErrorです。これは、undefinedまたはnullのオブジェクトのプロパティにアクセスしようとした時に発生します。

例:

let user = null;
console.log(user.name);

この場合、プロパティを持たないnullのnameプロパティにアクセスしようとしているため、JavaScriptはエラーを投げます。

デバッグ方法: - nullまたはundefinedの値をチェックする: プロパティにアクセスする前に、オブジェクトが存在することを常に確認します。

if (user) {
    console.log(user.name);
} else {
    console.log('ユーザーがnullまたはundefinedです。');
}
  • オプショナルチェイニングを使用する: モダンJavaScriptでは、この問題をよりクリーンに処理する方法を提供しています:
console.log(user?.name); // エラーを投げずにundefinedを返す

2. Uncaught ReferenceError: X is Not Defined

ReferenceErrorは、宣言されていない変数にアクセスしようとした時に発生します。このエラーは、変数のスコープやタイプミスを扱う際によく発生します。

例:

console.log(age); // Uncaught ReferenceError: age is not defined

デバッグ方法: - タイプミスをチェックする: 変数が正しくスペルされており、宣言と一致していることを確認する - 適切なスコープを確認する: 変数がブロックや関数内で宣言されている場合、使用しようとしているコンテキストからアクセス可能であることを確認する

let age = 30;
if (true) {
    console.log(age); // 正しく30をログ出力する
}
  • 使用前に変数を宣言する: このエラーを避けるため、変数を使用する前に必ずlet、const、またはvarで宣言する

3. SyntaxError: Unexpected Token

SyntaxErrorは、JavaScriptが構文規則に従っていないコード行に遭遇した時に発生します。これは、カンマ、括弧、または予期しないキーワードが欠落しているなど、様々な理由で発生する可能性があります。

例:

if (true {
    console.log('Hello');
}

ここでは、trueの後の閉じ括弧が欠落しているため、構文エラーが発生します。

デバッグ方法: - 欠落または余分な文字をチェックする: カンマ、括弧、中括弧などの記号が欠落または余分にないか注意深く確認する

if (true) {
    console.log('Hello');
}
  • シンタックスハイライトを持つエディタを使用する: Visual Studio CodeなどのIDEは構文の問題をハイライトし、エラーを見つけやすくする
  • コードを小さなブロックで実行する: 大規模なコードを扱う場合、問題のあるコードを特定するために、小さなブロックに分割して実行してみる

4. Uncaught RangeError: Maximum Call Stack Size Exceeded

RangeErrorは、終了条件なしで関数が再帰的に何度も呼び出され、コールスタックがオーバーフローした時に発生します。

例:

function recurse() {
    recurse();
}
recurse(); // Maximum call stack size exceeded

このコードは無限の再帰を作成し、ブラウザがRangeErrorを投げる原因となります。

デバッグ方法: - 再帰関数に終了条件を追加する: 無限ループを防ぐため、再帰関数には必ずベースケースまたは終了条件を設ける

function recurse(counter) {
    if (counter > 10) return;
    recurse(counter + 1);
}
recurse(0);
  • ループの無限条件をチェックする: 再帰関数に加えて、ループ(forやwhile)にも最終的に停止する終了条件があることを確認する

5. Uncaught TypeError: X is Not a Function

このエラーは、関数ではない変数やプロパティを関数として呼び出そうとした時に発生します。

例:

let user = { name: 'John' };
user(); // Uncaught TypeError: user is not a function

ここでは、JavaScriptがオブジェクトであるuserを関数として扱おうとしています。

デバッグ方法: - 変数の型をチェックする: 呼び出そうとしている変数が実際に関数であることを確認する

function greet() {
    console.log('Hello');
}
greet(); // 正しい使用法
  • 変数の型をログ出力する: 変数の型が不確かな場合、呼び出す前にtypeofを使用してチェックする:
console.log(typeof user); // "object"
  • 関数を上書きしていないことを確認する: エラーは時々、誤って関数が上書きされたことが原因で発生することがある

6. Uncaught SyntaxError: Unexpected End of Input

このエラーは、JavaScriptがスクリプトまたは関数の終わりに達したが、さらにコードを期待している場合に発生します。多くの場合、閉じ括弧、丸括弧、中括弧が欠落していることが原因です。

例:

function greet() {
    console.log('Hello');

この関数は閉じ中括弧が欠落しているため、コードが途中で終わってしまっています。

デバッグ方法: - 括弧や丸括弧の欠落を確認する: すべての開き括弧 {, (, [ などに対応する閉じ括弧があることを確認する

function greet() {
    console.log('Hello');
} // 正しい閉じ括弧
  • 適切なコードインデントを使用する: 適切なインデントは、括弧の不一致や閉じタグの欠落を簡単に見つけるのに役立つ
  • リンターを使用する: ESLintのようなリンターは、欠落や誤配置された構文要素を自動的に検出し、デバッグを容易にする

7. Uncaught Error: X is not iterable

このエラーは、反復可能でない値に対して反復処理を試みた時に発生します。反復可能なものには、配列、セット、その他の反復をサポートする構造が含まれます。

例:

let name = 'John';
for (let item of name) {
    console.log(item); // 文字列は反復可能なので動作する
}

let number = 123;
for (let item of number) {
    console.log(item); // Uncaught TypeError: number is not iterable
}

デバッグ方法: - データ型をチェックする: 反復しようとしている変数が配列や文字列などの有効な反復可能オブジェクトであることを確認する

let arr = [1, 2, 3];
for (let item of arr) {
    console.log(item); // 正しい使用法
}
  • 反復可能なオブジェクトに変換する: 必要に応じて、値を反復可能な構造に変換する:
let number = 123;
let numberArray = number.toString().split(''); // 反復可能な配列に変換
for (let num of numberArray) {
    console.log(num);
}

8. Uncaught TypeError: Assignment to Constant Variable

このエラーは、定数(const)変数に値を再代入しようとした時に発生します。定数として宣言された値は、一度宣言されると変更できません。

例:

const user = 'John';
user = 'Jane'; // Uncaught TypeError: Assignment to constant variable

デバッグ方法: - constの適切な使用を確認する: constは変更されない値に使用すべきです。値の再代入が必要な場合は、代わりにletを使用する

let user = 'John';
user = 'Jane'; // 正しい使用法
  • オブジェクトや配列のconstを慎重に使用する: constで宣言されたオブジェクトや配列は、変数自体の再代入はできませんが、プロパティや要素の変更は可能です
const user = { name: 'John' };
user.name = 'Jane'; // これは許可される

9. Uncaught SyntaxError: Unexpected Identifier

このエラーは、JavaScriptが予期しない場所で識別子(変数名など)に遭遇した時に発生します。多くの場合、演算子、カンマ、またはセミコロンの欠落が原因です。

例:

let user = 'John' let age = 30; // Uncaught SyntaxError: Unexpected identifier

このエラーは2つのステートメントの間にセミコロンが欠落していることが原因です。

デバッグ方法: - セミコロンの欠落をチェックする: JavaScriptではセミコロンは任意ですが、欠落していると問題を引き起こすことがあります

let user = 'John'; 
let age = 30; // 正しい構文
  • コードの配置を見直す: 変数宣言や関数呼び出しが予期しない場所にないことを確認する

10. Uncaught ReferenceError: Event is Not Defined

このエラーは、eventオブジェクトを明示的に宣言せずにイベントハンドラで使用した時に発生します。特に古いブラウザやイベントオブジェクトが暗黙的に渡されない状況で発生します。

例:

button.onclick = function() {
    console.log(event); // Uncaught ReferenceError: event is not defined
};

デバッグ方法: - イベントオブジェクトを明示的に渡す: イベントハンドラ関数でイベントオブジェクトを常に明示的に渡す:

button.onclick = function(event) {
    console.log(event); // これでeventは正しく定義される
};
  • モダンなイベントリスナーを使用する: addEventListenerを使用する場合、イベントオブジェクトはデフォルトで渡されます:
button.addEventListener('click', function(event) {
    console.log(event);
});

JavaScriptエラーの高度なデバッグテクニック

一般的な10個のJavaScriptエラーとそのデバッグ方法を説明しましたが、ここではさらに高度なデバッグテクニックを探ってみましょう。これらの方法は、開発プロセスの早い段階でエラーを捉え、開発者としての全体的な効率を改善するのに役立ちます。大規模なアプリケーションや小規模なプロジェクトに関わらず、デバッグの習得は時間を節約し、厄介なバグを追跡する際の frustration を減らすことができます。

1. ブラウザ開発者ツールの使用

モダンなウェブブラウザには、JavaScriptのデバッグをより簡単にする強力な開発者ツールが装備されています。Chrome、Firefox、Edgeなどのほとんどのブラウザには、JavaScriptコンソール、ソースインスペクタ、ネットワークタブが内蔵されており、コードの検査、エラーの追跡、パフォーマンスのボトルネックの理解が可能です。

JavaScriptコンソール: コンソールはすべてのJavaScriptエラーが表示される場所で、何かが壊れた時に最初に確認すべき場所です。また、console.log()を使用して変数や出力を表示し、プログラムの流れを追跡することもできます。

例:

console.log(user); // userの変数の値をチェック

デバッグ時には、コンソールはより詳細なスタックトレースも表示でき、エラーが発生した正確な場所と関数がどのように呼び出されたかを特定するのに役立ちます。

2. ブレークポイントとソース検査

ChromeのデベロッパーツールのSourcesタブ(またはFirefoxのDebugger)では、コードにブレークポイントを設定できます。ブレークポイントは特定の行でコードを一時停止させ、変数の検査、式の監視、1行ずつコードの実行を確認することができます。

ブレークポイントの設定方法: 1. ページで右クリックして「検証」を選択するかF12を押して開発者ツールを開く 2. Sourcesタブに移動 3. JavaScriptコードが含まれているファイルを見つける 4. ブレークポイントを設定したい行番号をクリックすると、その地点でコードが一時停止する

その後、変数にカーソルを合わせて現在の値を確認したり、コールスタックを調べたり、コードを1行ずつ実行して正確な動作を確認したりできます。

3. try...catchブロックの活用

JavaScriptはtry...catchブロックという強力なエラー処理メカニズムを提供しています。これにより、アプリケーション全体をクラッシュさせることなく、エラーを適切に処理することができます。一部のコードの実行を「try」し、エラーが発生した場合は「catch」して制御された方法で処理するという考え方です。

例:

try {
    let user = JSON.parse('無効なJSON');
} catch (error) {
    console.error('JSONのパース中にエラーが発生:', error);
}

この例では、catchブロックが無効なJSONによってスローされたエラーを捕捉し、アプリケーションのクラッシュを防ぎ、代わりにエラーをコンソールに記録します。

try...catchを使用するべき場面: - エラーが予想される外部データ(APIレスポンス、ファイルアップロードなど)を扱う時 - アプリケーション全体を停止させずに失敗を処理すべき重要なコードセクションを実行する時

4. debuggerキーワードの使用

debuggerキーワードは、ブラウザの開発者ツールが開いている時に自動的にブレークポイントをトリガーするJavaScriptの組み込みツールです。これは、何が問題なのかを調査するために特定の地点でコードを一時停止させたい時に便利です。

例:

function calculateTotal(price, taxRate) {
    debugger; // 開発者ツールが開いている時、ここでコードの実行が一時停止する
    return price * (1 + taxRate);
}

debuggerステートメントに遭遇すると、ブラウザはその行で実行を一時停止し、変数の検査やコードの残りの部分のステップ実行を可能にします。これは、手動でブレークポイントを設定することなく、コードの特定の部分のバグを素早く見つけるための優れた方法です。

5. ネットワークリクエストの監視

多くのJavaScriptアプリケーションは、AJAXやAPIコールを介して取得される外部データに依存しています。エラーは、失敗したネットワークリクエスト、APIから返された不正なデータ、または誤設定されたエンドポイントが原因で発生することがあります。ブラウザの開発者ツールには、ページによって行われたすべてのHTTPリクエストを監視できるNetworkタブが用意されています。

チェックできる項目: - リクエストステータス: リクエストは成功(ステータスコード200)したか?失敗(例:404、500)したか? - レスポンスデータ: サーバーから返されたデータは期待された形式(JSON、XMLなど)か? - タイミング: リクエストの完了に時間がかかりすぎて、タイムアウトの問題を引き起こす可能性はないか?

ネットワーク関連の問題をデバッグするには: 1. ブラウザの開発者ツールでNetworkタブを開く 2. AJAXリクエストに焦点を当てるためにXHRでフィルタリングする 3. 個々のリクエストをクリックしてヘッダー、レスポンス、その他の詳細を調査する

6. 非同期コードのデバッグ

非同期JavaScript(PromisesやAsync/Awaitを使用)は、特に複雑なイベントシーケンスを扱う場合、デバッグが難しい場合があります。処理されていないPromiseの拒否や失敗した非同期タスクは、サイレントな失敗を引き起こし、エラーの追跡を困難にする可能性があります。

例えば、Promiseチェーンでエラー処理を忘れると、有用な情報を提供しない未捕捉のエラーが発生する可能性があります。

例:

fetch('https://api.example.com/data')
    .then(response => response.json())
    .catch(error => console.error('データの取得中にエラーが発生:', error));

より複雑な非同期コードの場合、async/awaitを使用することでエラー処理を簡略化できます:

async function fetchData() {
    try {
        let response = await fetch('https://api.example.com/data');
        let data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('データの取得中にエラーが発生:', error);
    }
}

結論

JavaScriptのエラーは frustrating かもしれませんが、その原因を理解すれば解決は比較的簡単です。TypeError、ReferenceError、SyntaxErrorなどの一般的なエラーを認識し、それらをデバッグする方法を知ることで、問題を素早く効率的に解決できます。エラーメッセージを注意深く読み、タイプミス、スコープの問題、不正なデータ型などの一般的なミスをチェックすることから始めましょう。ブラウザのコンソールで利用可能なデバッグツールを使用し、コーディングプロセスの早い段階でエラーを捕捉するためにリンターやモダンなIDEの機能を活用することを躊躇しないでください。

プロアクティブなデバッグ戦略と適切なツールを使用することで、クリーンでエラーのないJavaScriptコードを書く道が開かれます。

©コハム