404 Not Found

404 Not Found


nginx

JavaScript エラーとデバッグ

バグのないコードを書くことは不可能です。JavaScript の作者でさえ10日間で typeof null === "object" のバグを作ってしまいました。そのため、バグの発見とエラー処理はコードの書き方を学ぶことよりも重要です。

📖 まとめ

一般的なエラータイプ

エラータイプ 原因
SyntaxError 無効な構文 if (true { — 括弧の不一致
ReferenceError 存在しない変数へのアクセス console.log(notExist)
TypeError 間違った型への操作 undefined.toString()
RangeError 有効範囲外の値 new Array(-1)

SyntaxError はコード実行前に(解析段階で)検出されます。他の3つはランタイムエラーです。

try...catch...finally

try...catch を使ってランタイムエラーをキャッチし、プログラムのクラッシュを防ぎましょう。

HTML
<script>
try {
  const data = JSON.parse('{bad json}');
} catch (error) {
  console.log("解析失敗:", error.message);
} finally {
  console.log("クリーンアップ処理");
}
</script>

catcherror オブジェクトには2つのよく使われるプロパティがあります。

throw — エラーを手動でスローする

try...catch はランタイムエラーしかキャッチできないため、ビジネスロジックのエラーは手動でスローする必要があります。

HTML
<script>
function setAge(age) {
  if (typeof age !== "number" || age < 0) {
    throw new Error("年齢は正の数でなければなりません");
  }
  console.log("年齢を設定しました: " + age);
}

try {
  setAge(-5);
} catch (e) {
  console.log(e.message);
}
</script>

throw は任意の値をスローできますが、new Error() またはそのサブクラスを使用することを推奨します。スタックトレース情報が含まれるためです。

カスタムエラーメッセージ

Error のサブクラスやカスタムプロパティを使って、より豊かなエラー情報を提供しましょう。

HTML
<script>
class ValidationError extends Error {
  constructor(field, message) {
    super(message);
    this.name = "ValidationError";
    this.field = field;
  }
}

try {
  throw new ValidationError("email", "無効なメール形式");
} catch (e) {
  console.log(e.name + " [" + e.field + "]: " + e.message);
}
</script>

ブラウザ DevTools(F12)

パネル 用途
Console ログの表示、コードの実行、エラーの確認
Sources ブレークポイントの設定、コードのステップ実行、変数の確認
Network ネットワークリクエスト、レスポンスステータス、読み込み時間の確認
Elements DOM とスタイルの表示・修正

ブレークポイントデバッグは最も強力なデバッグ手法です。Sources パネルで行番号をクリックしてブレークポイントを設定し、ページを更新するとコードがそのポイントで一時停止します。行ごとにステップ実行し、変数を確認し、コールスタックを調べましょう。

console ファミリー

メソッド 用途
console.log() 一般的な出力
console.warn() 警告(黄色)
console.error() エラー(赤色)
console.table() 配列/オブジェクトをテーブルで表示
console.time() / console.timeEnd() タイミング計測
console.group() / console.groupEnd() グループ化された出力
console.clear() コンソールのクリア

console.table はオブジェクトの配列を表示する際に特に便利です。log より遥かに読みやすいです。

デバッグのヒント


例:JSON 解析の try/catch

HTML
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>JSON 解析</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    .demo { max-width: 600px; margin: 16px auto; }
    textarea { width: 100%; height: 120px; padding: 12px; font-size: 14px; font-family: monospace; border: 2px solid #ccc; border-radius: 6px; resize: vertical; box-sizing: border-box; }
    button { padding: 10px 24px; font-size: 16px; border: none; border-radius: 6px; cursor: pointer; margin: 8px 4px; }
    .parse { background: #4a90d9; color: #fff; }
    .parse:hover { background: #357abd; }
    .bad { background: #d9534f; color: #fff; }
    .bad:hover { background: #c9302c; }
    .result { margin-top: 16px; padding: 16px; border-radius: 8px; }
    .success { background: #d4edda; border: 2px solid #5cb85c; }
    .error-box { background: #f8d7da; border: 2px solid #d9534f; }
    .error-type { font-weight: bold; color: #721c24; }
    .error-msg { margin-top: 4px; color: #721c24; }
  </style>
</head>
<body>
  <h2 style="text-align:center;">JSON 解析エラー処理</h2>
  <div class="demo">
    <textarea id="jsonInput">{
  "name": "Alice",
  "age": 20,
  "skills": ["JavaScript", "HTML", "CSS"]
}</textarea>
    <div style="text-align:center;">
      <button class="parse" id="parseBtn">JSON を解析</button>
      <button class="bad" id="badBtn">無効な JSON を挿入</button>
    </div>
    <div id="result"></div>
  </div>
  <script>
    function parseJSON(jsonStr) {
      try {
        const data = JSON.parse(jsonStr);
        return { success: true, data: data };
      } catch (error) {
        return { success: false, name: error.name, message: error.message };
      }
    }

    document.getElementById("parseBtn").addEventListener("click", function() {
      var input = document.getElementById("jsonInput").value;
      var result = parseJSON(input);
      var output = document.getElementById("result");

      if (result.success) {
        output.className = "result success";
        output.innerHTML = "<strong>解析成功!</strong><pre>" +
          JSON.stringify(result.data, null, 2) + "</pre>";
      } else {
        output.className = "result error-box";
        output.innerHTML = '<div class="error-type">' + result.name +
          '</div><div class="error-msg">' + result.message + '</div>';
      }
    });

    document.getElementById("badBtn").addEventListener("click", function() {
      document.getElementById("jsonInput").value = '{name: "Alice", age: 20}';
    });
  </script>
</body>
</html>
▶ 試してみよう

例:カスタムエラーとフォームバリデーション

HTML
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>カスタムエラー</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    .form { max-width: 440px; margin: 16px auto; padding: 24px; background: #f9f9f9; border-radius: 12px; }
    .row { margin: 12px 0; }
    label { display: block; margin-bottom: 4px; font-weight: bold; }
    input { width: 100%; padding: 10px 12px; font-size: 16px; border: 2px solid #ccc; border-radius: 6px; box-sizing: border-box; }
    input.valid { border-color: #5cb85c; }
    input.invalid { border-color: #d9534f; }
    .field-error { color: #d9534f; font-size: 13px; margin-top: 2px; min-height: 20px; }
    button { padding: 10px 24px; font-size: 16px; border: none; border-radius: 6px; cursor: pointer; background: #4a90d9; color: #fff; width: 100%; margin-top: 8px; }
    button:hover { background: #357abd; }
    .global-msg { margin-top: 12px; padding: 12px; border-radius: 6px; text-align: center; }
    .global-ok { background: #d4edda; color: #155724; }
    .global-fail { background: #f8d7da; color: #721c24; }
  </style>
</head>
<body>
  <h2 style="text-align:center;">カスタムエラー検証</h2>
  <div class="form">
    <div class="row">
      <label>ユーザー名(3〜20文字):</label>
      <input type="text" id="username" placeholder="ユーザー名を入力" />
      <div class="field-error" id="usernameErr"></div>
    </div>
    <div class="row">
      <label>年齢(0〜150):</label>
      <input type="text" id="age" placeholder="年齢を入力" />
      <div class="field-error" id="ageErr"></div>
    </div>
    <div class="row">
      <label>メールアドレス:</label>
      <input type="text" id="email" placeholder="メールアドレスを入力" />
      <div class="field-error" id="emailErr"></div>
    </div>
    <button id="submitBtn">送信</button>
    <div class="global-msg" id="globalMsg"></div>
  </div>
  <script>
    class ValidationError extends Error {
      constructor(field, message) {
        super(message);
        this.name = "ValidationError";
        this.field = field;
      }
    }

    function validateUsername(value) {
      if (!value.trim()) throw new ValidationError("username", "ユーザー名は空欄にできません");
      if (value.trim().length < 3) throw new ValidationError("username", "ユーザー名は3文字以上でなければなりません");
      if (value.trim().length > 20) throw new ValidationError("username", "ユーザー名は20文字以下でなければなりません");
    }

    function validateAge(value) {
      if (!value.trim()) throw new ValidationError("age", "年齢は空欄にできません");
      var num = Number(value);
      if (isNaN(num)) throw new ValidationError("age", "有効な数値を入力してください");
      if (num < 0 || num > 150) throw new ValidationError("age", "年齢の範囲:0〜150");
      if (!Number.isInteger(num)) throw new ValidationError("age", "年齢は整数でなければなりません");
    }

    function validateEmail(value) {
      if (!value.trim()) throw new ValidationError("email", "メールアドレスは空欄にできません");
      if (!value.includes("@")) throw new ValidationError("email", "メールアドレスには @ が必要です");
      if (!value.includes(".")) throw new ValidationError("email", "無効なメール形式です");
    }

    var validators = {
      username: validateUsername,
      age: validateAge,
      email: validateEmail
    };

    function clearErrors() {
      ["username", "age", "email"].forEach(function(field) {
        document.getElementById(field).className = "";
        document.getElementById(field + "Err").textContent = "";
      });
      document.getElementById("globalMsg").innerHTML = "";
      document.getElementById("globalMsg").className = "global-msg";
    }

    document.getElementById("submitBtn").addEventListener("click", function() {
      clearErrors();
      var hasError = false;

      ["username", "age", "email"].forEach(function(field) {
        var value = document.getElementById(field).value;
        try {
          validators[field](value);
          document.getElementById(field).className = "valid";
        } catch (e) {
          hasError = true;
          document.getElementById(field).className = "invalid";
          document.getElementById(field + "Err").textContent =
            e.name + ": " + e.message;
        }
      });

      var msg = document.getElementById("globalMsg");
      if (hasError) {
        msg.className = "global-msg global-fail";
        msg.textContent = "フォームにエラーがあります。修正してもう一度お試しください";
      } else {
        msg.className = "global-msg global-ok";
        msg.textContent = "すべてのフィールドが検証に合格しました!";
      }
    });
  </script>
</body>
</html>
▶ 試してみよう

例:エラータイプクイックリファレンスと console メソッド

HTML
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>エラータイプとコンソール</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    .section { margin: 20px 0; }
    table { border-collapse: collapse; width: 100%; max-width: 700px; }
    td, th { border: 1px solid #ddd; padding: 10px 14px; text-align: left; }
    th { background: #4a90d9; color: #fff; }
    .trigger { background: #f8d7da; }
    .safe { background: #d4edda; }
    button { padding: 6px 16px; border: none; border-radius: 4px; cursor: pointer; margin: 2px; font-size: 14px; }
    .btn-err { background: #d9534f; color: #fff; }
    .btn-safe { background: #5cb85c; color: #fff; }
    .btn-info { background: #5bc0de; color: #fff; }
    .output { margin-top: 12px; padding: 12px; background: #1a1a2e; color: #e0e0e0; border-radius: 6px; font-family: monospace; font-size: 13px; min-height: 60px; max-height: 200px; overflow-y: auto; }
    .log-line { margin: 2px 0; }
    .log-warn { color: #f0ad4e; }
    .log-error { color: #d9534f; }
    .log-info { color: #5bc0de; }
  </style>
</head>
<body>
  <h2>エラータイプと console メソッドのデモ</h2>

  <div class="section">
    <h3>一般的なエラータイプ</h3>
    <table>
      <tr><th>エラータイプ</th><th>トリガーコード</th><th>try/catch 結果</th></tr>
      <tr class="trigger"><td>SyntaxError</td><td><code>JSON.parse("{bad")</code></td><td id="r1"></td></tr>
      <tr class="trigger"><td>ReferenceError</td><td><code>notExistVar</code></td><td id="r2"></td></tr>
      <tr class="trigger"><td>TypeError</td><td><code>undefined.toString()</code></td><td id="r3"></td></tr>
      <tr class="trigger"><td>RangeError</td><td><code>new Array(-1)</code></td><td id="r4"></td></tr>
    </table>
  </div>

  <div class="section">
    <h3>console メソッド(シミュレート出力)</h3>
    <button class="btn-info" onclick="simLog('log', '通常のログ')">console.log</button>
    <button class="btn-info" onclick="simLog('warn', '警告メッセージ')">console.warn</button>
    <button class="btn-err" onclick="simLog('error', 'エラーメッセージ')">console.error</button>
    <button class="btn-info" onclick="simLog('info', '情報メッセージ')">console.info</button>
    <button class="btn-safe" onclick="simTable()">console.table</button>
    <button class="btn-safe" onclick="simTime()">console.time</button>
    <button class="btn-info" onclick="clearSim()">クリア</button>
    <div class="output" id="simConsole"></div>
  </div>

  <script>
    var tests = [
      function() { JSON.parse("{bad"); },
      function() { notExistVar; },
      function() { var x; x.toString(); },
      function() { new Array(-1); }
    ];

    var ids = ["r1", "r2", "r3", "r4"];
    var names = ["SyntaxError", "ReferenceError", "TypeError", "RangeError"];

    tests.forEach(function(fn, i) {
      try {
        fn();
        document.getElementById(ids[i]).textContent = "トリガーされず(予期しない)";
      } catch (e) {
        document.getElementById(ids[i]).innerHTML =
          '<span class="safe">' + e.name + ": " + e.message + '</span>';
      }
    });

    function simLog(type, msg) {
      var cls = type === "warn" ? "log-warn" :
                type === "error" ? "log-error" : "log-info";
      var prefix = type === "warn" ? "⚠" :
                   type === "error" ? "✖" : "ℹ";
      addLine(prefix + " " + msg, cls);
      if (type !== "error") {
        console[type](msg);
      }
    }

    function simTable() {
      var data = [
        { name: "Alice", age: 20, score: 90 },
        { name: "Bob", age: 22, score: 85 },
        { name: "Charlie", age: 21, score: 95 }
      ];
      addLine("console.table:", "log-info");
      data.forEach(function(d) {
        addLine("  " + d.name + " | " + d.age + " | " + d.score, "log-info");
      });
      console.table(data);
    }

    function simTime() {
      console.time("demo");
      var sum = 0;
      for (var i = 0; i < 1000000; i++) { sum += i; }
      console.timeEnd("demo");
      addLine("⏱ console.time/timeEnd: 1,000,000 回の加算完了", "log-info");
    }

    function addLine(text, cls) {
      var div = document.getElementById("simConsole");
      div.innerHTML += '<div class="log-line ' + (cls || "") + '">' + text + '</div>';
      div.scrollTop = div.scrollHeight;
    }

    function clearSim() {
      document.getElementById("simConsole").innerHTML = "";
    }
  </script>
</body>
</html>
▶ 試してみよう

❓ よくある質問

Q try...catch は SyntaxError をキャッチできますか?
A いいえ。SyntaxError は実行が始まる前の解析段階でスローされるため、try ブロックに到達しません。ただし、ランタイムで SyntaxError が発生した場合(例:JSON.parse の失敗、new Function("bad syntax"))はキャッチできます。
Q finally をいつ使うべきですか?
A エラーの有無に関わらずクリーンアップ操作を実行する必要がある場合です。ファイルを閉じる、リソースを解放する、ローディング状態を非表示にするなど。多くのシンプルなシナリオでは、catch だけで十分です。
Q throw と return の違いは何ですか?
A return は通常のフロー用です。呼び出し元が戻り値を処理します。throw は異常なフロー用です。後続のコードをスキップし、コールスタックを遡って catch を探します。エラーには throw を、正常な結果には return を使いましょう。

📝 演習

  1. safeDivide(a, b) 関数を書きましょう。try...catch を使ってゼロ除算を処理してください(ヒント:JS でゼロ除算してもスローされないため、自分でチェックしてスローする必要があります)。結果またはエラーメッセージを返してください。
  2. safeJSONParse(str) 関数を書きましょう。JSON 文字列を解析してください。失敗時は { success: false, error: <エラーメッセージ> } を、成功時は { success: true, data: <解析結果> } を返してください。
  3. retry(fn, times) 関数を書きましょう。fn を実行し、スローされた場合は最大 times 回再試行してください。すべての試行が失敗した場合は最後のエラーをスローしてください。try...catch で実装してください。
100%