JavaScript | ゼロからはじめるプログラミング、30日で基礎を学ぶJavaScript:Webページを操作できるようになる - Day22:デバッグ

JavaScript JavaScript
スポンサーリンク

Day22 後半のゴール

前半で「console.log で中身を見る」「コンソールでエラーを確認する」という入口は押さえました。
後半ではそこから一歩進んで、

どこから絞り込んでいくか
条件分岐や関数が絡んだときにどう追いかけるか
「原因が分からない」をどう分解していくか

ここを、具体的なバグ付きコードを題材にしながら掘り下げていきます。


条件分岐が思ったとおりに動かないとき

例題1:if が「通るはずなのに通らない」

次のコードを見てください。

<input id="ageInput" type="text" placeholder="年齢を入力">
<button id="checkButton">判定</button>
<p id="result"></p>

<script>
  const ageInput = document.getElementById("ageInput");
  const checkButton = document.getElementById("checkButton");
  const result = document.getElementById("result");

  checkButton.addEventListener("click", () => {
    const age = ageInput.value;

    if (age >= 20) {
      result.textContent = "成人です";
    } else {
      result.textContent = "未成年です";
    }
  });
</script>

見た目は正しそうですが、
「20」と入力しても「未成年です」と出る、というバグが起きることがあります。

ここでいきなり if 文を書き換えない。
まずは console.log で「age が何なのか」を確認します。

checkButton.addEventListener("click", () => {
  const age = ageInput.value;

  console.log("age の値:", age, "型:", typeof age);

  if (age >= 20) {
    result.textContent = "成人です";
  } else {
    result.textContent = "未成年です";
  }
});
JavaScript

コンソールには例えばこう出ます。

age の値: 20 型: string

ここで重要なのは「文字列の 20 と数値の 20 は違う」という事実です。
比較自体は動きますが、思わぬところでおかしくなりやすい。
だから、明示的に Number に変換するのが安全です。

const age = Number(ageInput.value);
console.log("変換後 age:", age, "型:", typeof age);
JavaScript

この「if の前で値と型を確認する」というのが、条件分岐デバッグの基本パターンです。


関数が増えたときのデバッグ

例題2:どの関数でおかしくなっているのか分からない

次のようなコードを考えます。

function parsePrice(text) {
  return Number(text);
}

function calcTotal(priceText, countText) {
  const price = parsePrice(priceText);
  const count = parsePrice(countText);
  return price * count;
}

function showTotal(total) {
  const result = document.getElementById("result");
  result.textContent = `合計:${total}円`;
}

const priceInput = document.getElementById("priceInput");
const countInput = document.getElementById("countInput");
const button = document.getElementById("button");

button.addEventListener("click", () => {
  const total = calcTotal(priceInput.value, countInput.value);
  showTotal(total);
});
JavaScript

ここで、「なぜか合計が NaN になる」というバグが出たとします。
どこから見ればいいか分からない、という状態になりがちです。

こういうときは「上流から順番に」console.log を仕込んでいきます。

まずはイベントハンドラの直後。

button.addEventListener("click", () => {
  console.log("入力値:", priceInput.value, countInput.value);
  const total = calcTotal(priceInput.value, countInput.value);
  console.log("calcTotal の結果:", total);
  showTotal(total);
});
JavaScript

ここで、入力値がそもそもおかしくないかを確認します。
次に、calcTotal の中身を覗きます。

function calcTotal(priceText, countText) {
  console.log("calcTotal 受け取った値:", priceText, countText);
  const price = parsePrice(priceText);
  const count = parsePrice(countText);
  console.log("parse 後:", price, count);
  return price * count;
}
JavaScript

さらに parsePrice にも入れてみる。

function parsePrice(text) {
  console.log("parsePrice に渡された text:", text);
  return Number(text);
}
JavaScript

このように「どこを通って、どこでおかしくなったか」を
上から順に絞り込んでいくのが、関数が増えたときのデバッグの基本です。

一気に「原因」を当てにいくのではなく、
「ここまでは正しい」「ここから先で壊れている」を区切っていくイメージです。


典型的なエラーを読めるようになる

例題3:undefined のプロパティを読もうとしている

よく見るエラーのひとつがこれです。

Uncaught TypeError: Cannot read properties of undefined (reading 'length')

例えば、次のようなコードで起きます。

function showFirstChar(text) {
  const first = text[0];
  console.log("1文字目:", first);
}

let value;
showFirstChar(value);
JavaScript

コンソールには上のようなエラーが出て、
どこかに script.js:3 のような行番号が表示されます。

ここでやることは二つです。

どの行でエラーが起きているかを見る
その行で使っている変数の中身を console.log で確認する

この例なら、showFirstChar の最初にこう書きます。

function showFirstChar(text) {
  console.log("text の中身:", text);
  const first = text[0];
  console.log("1文字目:", first);
}
JavaScript

コンソールには text の中身: undefined と出るはずです。
つまり、「text が undefined なのに text[0] を読もうとしている」ことが分かります。

ここまで分かれば、修正すべきは「呼び出し側」です。

let value = "";
showFirstChar(value);
JavaScript

あるいは、関数側で「undefined のときは何もしない」ガードを書くこともできます。

function showFirstChar(text) {
  if (typeof text !== "string") {
    console.log("文字列ではありません:", text);
    return;
  }
  const first = text[0];
  console.log("1文字目:", first);
}
JavaScript

エラーメッセージは「何が undefined だったか」を教えてくれるので、
そこに console.log を足して「実際の中身」を確認する、という流れを覚えておいてください。


「何も起きない」バグの追い方

例題4:イベントがそもそも発火していない

初心者がよくハマるのが「クリックしても何も起きない」です。
エラーも出ていないように見えるので、余計に迷います。

例えば、次のようなコード。

<button id="runButton">実行</button>
<p id="result"></p>

<script>
  const button = document.getElementById("runbutton");
  const result = document.getElementById("result");

  button.addEventListener("click", () => {
    result.textContent = "実行しました";
  });
</script>

id のスペルが runButton と runbutton で微妙に違う、というパターンです。

こういうときにまずやるのは、イベントハンドラの一行目に console.log を置くことです。

button.addEventListener("click", () => {
  console.log("クリックされました");
  result.textContent = "実行しました";
});
JavaScript

クリックしても「クリックされました」がコンソールに出ないなら、
そもそも addEventListener が正しく設定されていない可能性が高い。
そこで、button 自体を確認します。

console.log("button の中身:", button);
JavaScript

null と出たら、「取得に失敗している」と分かります。
あとは HTML と JavaScript の id を見比べて、スペルミスに気づけるはずです。

「何も起きない」ときは、
イベントハンドラの先頭に console.log を置く
イベントを登録している要素自体を console.log で確認する

この二段構えで攻めると、だいたい原因にたどり着けます。


console.log を「消す/残す」の判断

開発中はガンガン書いていい

開発中は、console.log を遠慮なく書いていいです。
むしろ「足りないくらい」のことが多い。
値の流れ、条件の結果、関数の呼び出し順など、
気になるところにはどんどんライトを当ててください。

仕上げのときに整理する

ただし、完成に近づいてきたら、一度落ち着いて見直します。

本当に必要なログか
デバッグ用にしか使っていないログか

デバッグ専用の console.log は、基本的には消すかコメントアウトします。
残すとしても、メッセージを分かりやすくしておくと、
後で自分や他人が見たときに役立ちます。

セキュリティの観点でも、
ユーザーの入力内容や内部状態を詳細にログに出しっぱなしにするのは好ましくありません。
「本番で見られて困るログ」は残さない、という意識も持っておいてください。


Day22 後半のまとめ

後半で深掘りしたのは、デバッグの「絞り込み方」です。

条件分岐の前で値と型を console.log する
関数が多いときは、上流から順にログを仕込んでいく
undefined / null エラーは「そもそも中身がない」サインとして読む
「何も起きない」ときは、イベントハンドラの先頭にログを置いて発火を確認する
開発中はたくさんログを書き、仕上げで整理する

ここまでできるようになると、「バグが怖いもの」ではなくなります。
バグは必ず出ます。
大事なのは「出たときに、落ち着いて追いかける道具と習慣を持っているか」です。
Day22 で身につけたこの感覚は、この先ずっとあなたを助けてくれます。

タイトルとURLをコピーしました