JavaScriptのデバッグ入門

10 min 51 views
Debag

「動かない…なんで?」を自分で解決するための基本とツールの使い方

JavaScriptを書いていると、ほぼ100%の確率で「思ったとおりに動かない…」という瞬間がきます
それはあなただけじゃなくて、現場のエンジニア全員そうです。

むしろ「どう壊れているかを調べて直す力(=デバッグ力)」こそが実務でめちゃくちゃ重要になってきます。

この章では次のことをまとめて学びます。

  • どんな種類のエラーがあるのか
  • エラーをどんな流れで探していけばいいのか
  • ブラウザの開発者ツール(デベロッパーツール)をどう使えばいいのか
  • 実際にコードを止めて、値をのぞき見する方法(ブレークポイント / debugger)

「バグを直すのが怖い → バグが来ても落ち着いて原因を切り分けられる」に変えていきましょう。

デバッグって何をすること?

デバッグとは、ざっくりいうと

  1. おかしな挙動(バグ・エラー)を見つけて
  2. なんでそうなってるか原因を突き止めて
  3. 直す

という一連の作業のことです。

「デバッグできるようになる=詰まったときに自力で前に進めるようになる」なので、これは本当に強いです。

エラーには大きく2種類ある

JavaScriptでは、エラーといっても全部同じではありません。よく出てくるのは次の2タイプです。

 1. 文法エラー(構文エラー)

  • 書き方そのものが間違っていて、そもそも実行できない状態。
  • たとえばカッコが閉じていないとか、const のつもりが cnots とタイプミスしたとか。
  • ブラウザのコンソールに SyntaxError: ... のようなエラーメッセージが出ます。
  • どこで壊れてるかをブラウザが教えてくれるので、比較的直しやすいです。

イメージ:

function hello( {
  console.log("Hi!");
}
// ↑ カッコが合ってないので構文エラー

 2. ロジックエラー(実行時の間違い)

  • コード自体は動く。でも「期待した結果になってない」パターン。
  • たとえば計算がずれている、想定外の値が入っている、if の分岐が間違ってる、など。
  • 実行はできるのでブラウザは「壊れてますよ」とは教えてくれないこともあります。
  • これがいちばん時間がかかるし、心を削ってくるところです。

イメージ:

function add(a, b) {
  return a - b; // 本当は足し算したいのに引き算してる
}
console.log(add(5, 3)); // 期待は8なのに2になる

この「ロジックエラー」をつぶすときに本気で役に立つのが、ブラウザに内蔵されている デベロッパーツール(開発者ツール) なんです。

デベロッパーツールとは?

Chrome や Edge などのブラウザには、開発者向けのツールセットが最初から入っています。
これが「デベロッパーツール(開発者ツール / DevTools)」と呼ばれるものです。

開き方の例(Chrome / Edge 共通):

  • Windows: F12 キー、または右クリック →「検証」
  • Mac: ⌥Option + ⌘Command + I(もしくは右クリック →「検証」)

このツールでできることの一部:

  • コンソール(Console)で値を出力・確認する
  • 実際に動いているJavaScriptを途中で止めて、中身をのぞく
  • 1行ずつ実行して「どこでおかしくなったか」を追跡する
  • その瞬間の変数の中身を見る

つまり「なんで壊れてるか」を調べるためのレントゲンです。

デバッグの第一歩:console.log()とalert()

エラーの原因を調べたいとき、まず多くの人がやるのが「値を出して確認する」です。

 console.log(…)

const price = 1200;
console.log("priceの中身:", price);

こうすると、デベロッパーツールの Console(コンソール)タブ に値が表示されます。
「このタイミングでこの値が入ってるんだな」を見るのに便利。

 alert(…)

alert("ここまで処理きたよ!");

これは画面上にポップアップを出す方法です。

DevToolsを開いていなくても確認できるので、超シンプルな確認には便利。

ただし何回も出るとストレスなので乱用はしない感じです。

 でも…

ログをガンガン仕込んでいくやり方は、数行ならいいんですが、処理が長くなると逆に追いにくくなりますよね。
もっとスマートに「途中で止めて中身を見る」ことができたら最高です。

それが次の「debugger」と「ブレークポイント」です。

コードを途中で止める:debugger; を入れる

debugger; という1行を書くと、その行でプログラムの実行がピタッと止まります。(デベロッパーツールを開いた状態で有効)

止まった時点で、変数の中身や処理の流れをじっくり観察することができます。

 サンプルHTML(入力フォーム+JS)

<!DOCTYPE html>
<html>
  <head>
    <title>フォーム入力のハイライト</title>
    <link rel="stylesheet" href="./style.css">
  </head>
  <body>
    <div id="panel">
      <form>
        <div class="form-field">
          <label for="user_name">ユーザー名:</label>
          <input type="text" id="user_name">
        </div>

        <div class="form-field">
          <label for="password">パスワード:</label>
          <input type="password" id="password">
        </div>

        <div class="form-field">
          <label for="pref">都道府県:</label>
          <select id="pref">
            <option value="1">北海道</option>
            <option value="2">青森県</option>
            <option value="3">岩手県</option>
            <option value="4">秋田県</option>
            <option value="5">宮城県</option>
            <option value="6">福島県</option>
          </select>
        </div>

        <div class="form-field">
          <label for="comment">その他(自由記入)</label>
          <textarea id="comment" rows="5" cols="50"></textarea>
        </div>
      </form>
    </div>

    <script src="./focus_debug_sample.js"></script>
  </body>
</html>

 サンプルJS(debugger; で止める)

// フォーカスが当たったときの処理
function handleFocus(evt) {
  debugger; // ←ここで実行が一時停止する

  // いったん全部のclassをリセット
  for (const el of fields) {
    el.removeAttribute("class");
  }

  // 今フォーカスが当たった要素だけclassを付ける
  evt.target.setAttribute("class", "is-active");
}

// 入力系の要素をまとめて取得(input, select, textarea)
const fields = document.querySelectorAll("input, select, textarea");

// 全部に「フォーカスされたら handleFocus を呼ぶ」よう登録
for (const item of fields) {
  item.addEventListener("focus", handleFocus);
}

 実際にやる手順はこうなります

  1. ブラウザでこのページを開く
  2. デベロッパーツール(開発者ツール)を開く
  3. フォームのどこかをクリックしてフォーカスする
  4. debugger; の行で実行が止まる

止まっている間は、DevToolsのConsoleタブで fieldsevt.target などの変数名を入力すると、その瞬間の中身を確認できます。

つまり、

  • どの要素が拾えてる?
  • ちゃんとclass外れてる?
  • どの要素に is-active 付こうとしてる?
    をリアルタイムで覗ける、ってことです。

ブレークポイントでコードを止める(手動でストップ位置を指定する)

debugger; を書き込まなくても、ブラウザ側で「この行で止めて」と指定することもできます。

これがブレークポイントです。

ブレークポイントを使うと、

  • 好きな行で実行を一時停止できる
  • 止めた時点での変数の値をチェックできる
  • そこから1行ずつ動かせる(ステップ実行)
  • 関数の呼び出し経路(どこから呼ばれたか)まで見れる

という、かなり本格的なデバッグができます。

 ブレークポイントの練習用サンプル

これは簡単な「四則演算フォーム」です。演算子(+、-、×、÷)と2つの数字を入力すると結果を表示します。

HTML

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>四則演算デバッグ練習</title>
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <form>
      <h2>四則演算テスター</h2>

      <input type="number" id="val1" />
      <select id="op">
        <option value="add">+</option>
        <option value="sub">-</option>
        <option value="mul">×</option>
        <option value="div">÷</option>
      </select>
      <input type="number" id="val2" />
      =
      <span id="answer"></span>
    </form>

    <script src="./calc_debug_sample.js"></script>
  </body>
</html>

CSS(見やすさ用)

* {
  font-size: 24px;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
  background: #eeeeee;
  margin: 0;
  overflow: hidden;
}

form {
  margin-top: 20px;
  zoom: 2;
  text-align: center;
}

input[type="number"] {
  width: 30px;
}

#answer {
  width: 30px;
  display: inline-block;
}

JavaScript

// 入力が変わったら計算しなおす関数
function refreshResult() {
  // 計算結果を作る
  const out = doMath(
    Number(firstNum.value),   // 1つ目の数字
    Number(secondNum.value),  // 2つ目の数字
    operatorSelect.value      // 選択中の演算子
  );

  // 計算結果を画面に表示
  outputArea.innerHTML = out;
}

// 計算の中身
function doMath(a, b, kind) {
  let result;

  switch (kind) {
    case "add":
      result = plus(a, b);
      break;
    case "sub":
      result = minus(a, b);
      break;
    case "mul":
      result = times(a, b);
      break;
    case "div":
      result = divide(a, b);
      break;
  }

  return result;
}

// 足し算
function plus(a, b) {
  return a + b;
}

// 引き算
function minus(a, b) {
  return a - b;
}

// 掛け算
function times(a, b) {
  return a * b;
}

// 割り算
function divide(a, b) {
  return a / b;
}

// それぞれの要素を取得
const operatorSelect = document.getElementById("op");
const firstNum       = document.getElementById("val1");
const secondNum      = document.getElementById("val2");
const outputArea     = document.getElementById("answer");

// 入力や選択が変わったときに計算し直すようイベント登録
operatorSelect.addEventListener("change", refreshResult);
firstNum.addEventListener("change", refreshResult);
secondNum.addEventListener("change", refreshResult);

ブレークポイントの置き方(Chrome / Edge)

  1. デベロッパーツールを開く
  2. 上部のタブから Sources をクリック
  3. 左側にあるファイル一覧(Pageなどと表示されているツリー)から calc_debug_sample.js を選ぶ
  4. 右側にソースコードが表示されるので、止めたい行番号をクリックする

すると、その行に青いマーク(または矢印・●の印)がつきます。
これがブレークポイントです。

たとえば switch (kind) { ... } の行あたりにブレークポイントを置いておくと、どの演算が選ばれたのか、ab に何が入っているのかをその瞬間でチェックできます。

止まっているときに何ができるの?

ブレークポイントで実行が止まった状態になると、デベロッパーツールの右側パネルなどでこんなことができます。

 1. 変数の中身を確認する

  • ソースコード上でマウスカーソルを変数名に乗せると、その時点の値がポップアップで表示されます。
  • Consoleタブで変数名を直接入力しても、その値が見られます。

「いま firstNum.value はちゃんと数字になってる?」
kind は ‘add’ のはずだけど違う値入ってない?」
といった確認が一発でできます。

 2. Scope(スコープ)を見る

DevToolsには「Scope」みたいな領域があり、今実行中の関数内で使える変数(ローカル変数など)が一覧で見られます。
複雑なオブジェクトの中身も展開して追えるので、フォームの値や処理途中の一時変数を丁寧に見たいときに便利です。

 3. Call Stack(コールスタック)を見る

「この関数ってどこから呼ばれたんだっけ?」という呼び出し履歴も見られます。
とくにプロジェクトが大きくなると、「誰がこの関数叩いてるの?」が分からなくなるので、ここはかなり役立ちます。

一時停止した後の操作(ステップ実行)

止まったあとの画面には、再生ボタンや矢印アイコンが並びます。

これらで“どんな進め方をするか”をコントロールできます。

よく使うものだけ押さえておきましょう:

  • ▶(再開 / Resume)
    • 今止まってるところからプログラムを再開します。
    • 次のブレークポイントがあれば、そこでまた止まります。
  • ⤵ Step Over(ステップオーバー)
    • 今の行を実行して、次の行でまた止まります。
    • その行が関数呼び出しだったとしても、中身の中には入らず、ひとまとめで「呼んだことにして」進んでくれるイメージです。
    • 「ざっくり流れを追いたいとき」に便利。
  • ⤴ Step Into(ステップイン)
    • 関数呼び出しの行だった場合、その関数の中に潜って、1行ずつ見に行きます。
    • 「この関数の中でバグってそうなんだよな…」というときに使います。
    • かなり細かく追いかけたいとき向け。
  • ↩ Step Out(ステップアウト)
    • 今入っている関数の残りを一気に実行し、呼び出し元に戻ります。
    • 深いところに潜りすぎたからとりあえず抜けたい、というときに便利。

この「1行ずつ確かめる」っていう感覚は、はじめて触ると地味に感動します。
何がいつ実行されるのか、頭の中じゃなくて実際の流れで理解できるようになるからです。

まとめ(ここを押さえておくと安心)

  • バグは必ず出ます。むしろ「バグの探し方」を知ってる人が強いです。
  • エラーには「文法エラー(すぐ気づける系)」と「ロジックエラー(動くけどおかしい系)」がある。
  • console.log()alert() は、どこまで動いてるか・何が入っているかを手早く確認するための基本の道具。
  • もっと丁寧に調べたいときは、開発者ツールを開いて、
    • debugger; で好きな場所で停止させる
    • ブレークポイントで任意の行に停止ポイントを置く
    • 変数の中身をリアルタイムで見る
    • ステップ実行で1行ずつ動きを追いかける
  • これができるようになると「なんか動かないから一旦やり直そう…」ではなく、「どこからおかしいのか順番に見よう」に変わります。これは本当に大きな差になります。

いま「正直ちょっとむずかしそうかも」と感じたとしても大丈夫です。
このあとのフェーズ(フォーム処理・API通信・DOM操作・イベント処理など)に進むと、デバッグは必ず必要になります。

そこで少しずつ触っていけば手になじんでいきます。

焦らなくて大丈夫です。

関連記事