ブレークポイントを使ったJavaScriptデバッグを整理してみた【再入門】


はじめに

プログラミングの上達において、デバッグスキルを上げることはとても重要で近道の1つだと考えています。
私自身、勉強し始めた頃に知っていれば(理解できていれば)とよく思います。

今回、JavaScriptデバッグについてChromeDevtoolsとブレークポイントを使った基本パターンを整理しました。
自身の復習かつ、あまり馴染みの無い方でも、以下おおよそ理解できるようになれば良いなぁ、というのが本稿の目的です。

  • どのようなものにブレークポイントが貼れるのか
  • どういった時にブレークポイントが発動されるのか
  • ブレークポイントが発動されると何ができるのか

ご存知の方には特に目新しいことはないかと思いますが、何かのお役に立てば幸いです。

  • 表示・動作はChrome 50.0.2661.87mで確認したものになります(2016-05-11)
  • タブやパネルの位置は環境によって異なる可能性があります
  • Firebugなど他ツールでも似たようなことはできるかもしれません(未確認)

目次

  • ブレークポイントとは
  • Devtoolsのデバッグパネル
    • Watch機能で特定の変数を追う
  • DOMにブレークポイントを貼る
    • 設定方法
    • Subtree Modified
    • Attribute Modified
    • Node Removed
    • 注意事項
  • イベントにブレークポイントを貼る
    • 設定方法
  • スクリプトにブレークポイントを貼る
    • 設定方法
  • XHRにブレークポイントを貼る
    • 設定方法
  • 終わりに

ブレークポイントとは

そもそもブレークポイントとは何でしょうか。wikipediaによると、

ブレークポイント(英: breakpoint)は、ソフトウェア開発のデバッグ作業において実行中のプログラムを意図的に一時停止させる箇所である。 by wikipedia

だそうです。思ったより分かりやすいですね。
一時停止させることで、その場所での変数や関数呼び出しなど段階をおって処理の内容やフローを細かく確認できる機能、といったところでしょうか。

ブレークポイントはDevtoolsを使って簡単に設定することができ、発動中は該当箇所が表示されたり、前後に実行される関数が見えたり、とても便利です。ブレークポイントは大きく分けて以下4パターン貼ることができます。

  • DOM
  • イベント
  • スクリプト(ソースコード)
  • XHR

他にも例外・エラー時に発動する Pause on Exceptions があります。
https://developer.chrome.com/devtools/docs/javascript-debugging?hl=ja#pause-on-uncaught-exceptions

まずはDevtoolsでのデバッグ情報について整理します。
次に各種ブレークポイントについて見ていきたいと思います。

Devtoolsのデバッグパネル

Sourcesタブの右パネルに、デバッグ情報が表示されます。
基本的にはここを確認・操作してデバッグ作業を行っていきます。

よく使いそうな箇所をピックアップしてみます。

▼デバッグパネル

  • ①: 次の行へ。関数の場合は実行するが内部には移動しない
  • ②: 次の行へ。関数の場合は内部に移動する
  • ③: その関数を抜ける(処理は進む)
  • ④: 非同期処理をCall Stack(⑤)に含める
  • ⑤: 現時点で呼ばれた関数の順番
  • ⑥: 現時点での参照範囲(ローカル・グローバル・クロージャ)
  • ⑦: 該当するソースコード

①の左にある、|▶ (Resume script execution) は一時停止を解除し、次のブレークポイントまで進みます。

⑤のCall Stackでコードのファイル・位置を切り替えられるため処理の流れが把握できます。

▼処理の流れ

jQueryなどフレームワーク内での処理はStackから無視したいことは多いかと思います。
Call Stack右クリック→ Blackbox script でこのフレームワーク内処理をグレーアウトや非表示にできます。
(ブレークポイントの貼り方や実行タイプで、Call StackでのBlackboxの表示が少し異なる模様)
Blackboxとして登録されたフレームワーク情報は、setting > Blackboxingで確認できます。

▼フレームワークを消したパネル

▼Blackbox情報

⑦のコードパネルでは、選択行(Step)に応じた変数の確認などができます。

▼カーソルを合わせると変数の中身がポップアップ

Watch機能で特定の変数を追う

パネル内の Watch では監視したい変数や条件などが設定できます。
設定した値は実行する行を移動(Step)しても表示され続けるため、変化を確認し続けることができます。

▼Watchに変数を指定して値の変化を監視

DOMにブレークポイントを貼る

要素に対してブレークポイントを貼ることができます。
「要素がどうなるかは(見れば)大体分かるが、この処理がどこで行われているのか知りたい」といった時に使えそうです。

設定方法

Elementタブで右クリック→Break on でタイプを選択します(複数可)。

  • Subtree Modified
  • Attribute Modified
  • Node Removed

名前から想像つくとは思いますが、それぞれブレークポイントの発動条件が異なります。
また貼られたブレークポイントとタイプは右パネルのDOM Breakpointsで確認・toggleすることができます。

▼DOMにブレークポイントを貼る

Subtree Modified

  • 設定した要素の子要素自体に変更があった場合に発動します
  • 子要素とはテキストノードを含みます
  • 例えば、jQueryでいうと以下のようなメソッドに反応します
  • なお子要素のAttributeの変更はキャッチされません(子要素に直接Attribute Modifiedを貼る)
- text()
- html()
- empty()
- append()
- prepend()

※子要素にならないbefore(), after() などは発動しません

Attribute Modified

  • 要素のスタイルや属性に変更があった場合に発動します
  • 例えば、jQueryでいうと以下のようなメソッドに反応します
- addClass()
- removeClass()
- css()
- fadeIn/Out
- slideUp/down
- show/hide
- ...

Node Removed

  • 要素自体が削除された場合に発動します
  • 例えば、jQueryでいうと以下のようなメソッドに反応します
- remove()

display:none;empty()では要素自体は残っているため発動しません

イベントにブレークポイントを貼る

イベントに対してブレークポイントを貼ることができます。
「色んなイベント発生時に、どこでどんな処理が行われているか知りたい。スクロール、リサイズなどでの処理フローを確認したい」といった時に使えそうです。

設定方法

Sourcesタブの右パネルの Event Listener Breakpoints で操作します。
例えばMouse>clickにチェックを入れると、clickイベント発火時にブレークポイントが発動します。

▼clickイベントにブレークポイントを貼る

DOMやスクリプトを選択しなくても発動するので、ちゃちゃっと確認したい時にも使えそうです。

▼ブレークポイント発動イメージ

スクリプトにブレークポイントを貼る

ソースコードに直接ブレークポイントを貼ることができます。
「ここの処理がいつ、どんなフローで実行されているか確認したい。ここのタイミングで、この処理(変数など)がどうなっているのか確認したい」といった時に使えそうです。

設定方法

Sourcesタブの左パネルでjsファイルを選択し、中パネルにコードを表示させます。
左の行数をクリックしてブレークポイントを貼ることができます。

▼スクリプトにブレークポイントを貼る

後はブレークポイントを貼った行が呼び出される条件を満たせば発動します。
細かくポイントを指定できるので、一番直感的に操作できるかもしれません。

XHRにブレークポイントを貼る

XMLHttpRequest通信を行うタイミングでもブレークポイントを貼ることができます。
特定のURLにsendした時、と条件を絞ることができます。
「このファイルへのリクエストはどこで処理されているのか確認したい」といった時に使えそうです。

設定方法

  • Sourcesタブの右パネル XHR Breakpoints チェックを入れて制御します
  • 「+」マークを押して、対象としたいURLを追加することができます
  • 消したい時は右クリックからremoveを選択します

▼XHRにブレークポイントを貼る

例えば以下のようなコードがあったとします。
読み込み時にinc.html、ボタン押下時にinc.html + inc2.htmlへリクエストが走ります。

デバッグとしてinc2.htmlにリクエストを送った時の処理を確認したいとします。
上記の画像のように URL contains "/inc2.html" をチェックしていれば、ボタン押下時のみブレークポイントが発動します。

サンプルコード.js
function fnc_ajax_dmy() {
  var request = $.ajax({
    type: 'GET',
    url: "/inc.html",
    dataType: 'html',
    success: function(data, textStatus) {
      $('#js-show').append('<p>これはダミーだ!</p>');
    }
  });
}

function fnc_ajax() {
  var request = $.ajax({
    type: 'GET',
    url: "/inc.html",
    dataType: 'html'
  });
  // 処理成功時に実行
  request.done(function(data1, textStatus) {
    $.ajax({
      type: 'GET',
      url: "/inc2.html",
      dataType: 'html',
      success: function(data2, textStatus) {
        $('#js-show').append('<p>'+data1+'<br>'+data2+'</p>');
      }
    });
  });
}

fnc_ajax_dmy(); //inc.htmlをGET
$('button').on('click', fnc_ajax); //inc.html, inc2.htmlをGET
  • 処理箇所に直接ブレークポイントを貼っても良いのですが、コードの場所自体が不明という前提で...
  • Blackboxに登録していてもその中身がCall Stackに表示されてしまい、無駄なStep Overがあります(無視できないのかな?)

終わりに

基本的なことも多いですが、改めて整理してみるといい勉強になりますね。
また他にもこんなのがあるよ!とか、こういった時にも活用できまっせ!などあれば、ぜひ教えてください!