クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【デベロッパーツール 】JSコードのデバッグ(後編:ステップ実行編)

デベロッパーツールの続きです。JSコードのデバッグ、前回はブレークポイントを設定しました。今回は次の工程、ステップ実行編です。処理をブレークすると何が嬉しいのか、ステップを実行すると何がわかるのか。実際にJSコードをデバッグしながら紐解いていきます。それではいきましょう!

【目次】

※参考:【デベロッパーツール 】JSコードのデバッグ(中編:ブレークポイント設定編) - クモのようにコツコツと

※参考:Web開発環境の記事まとめ
qiita.com

今回使うサンプル

エラーが起きていないコード

今回のステップ実行の題材にするサンプルがこちら。

See the Pen fetch api 03 by イイダリョウ (@i_ryo) on CodePen.

(全画面表示)
※参考:CodePen - fetch api 03

「はっぴいえんど」のアルバム情報外部のjsonファイルから読み込んでDOMに入れている。

<h1 class="bandname">はっぴいえんど</h1>
<div class="spec">
  <section class="album">
    <h2 class="title"></h2>
    <p class="release"></p>
    <ol class="track"></ol>
  </section>
</div>
</section>

元のHTMLに入っているテキストはh1タグの「はっぴいえんど」のみ。

JSコード

//jsonファイルURL
var dmain = 'dl.dropboxusercontent.com';
var directory = 's/x6tsr4ah8cfe9ta';
var file = 'happyend';
var url = 'https://' + dmain + '/' + directory + '/' + file + '.json';

//json読み込み
  fetch(url)
    .then(function(response) {
      return response.json();
    })
    .then(function(json) {

    //外枠アクセス
    var spec = document.querySelector('.spec');

    //テンプレ複製
    //(アルバム枚数-1枚)
    for (var i=1; i < json.length; i++) {
      var clone = spec.firstElementChild.cloneNode(true);
      spec.appendChild(clone);
    }
    
    //タイトル、発売日、曲名アクセス
    var specTitle = document.querySelectorAll('.title');
    var specRelease = document.querySelectorAll('.release');
    var specTrack = document.querySelectorAll('.track');
    
    //アルバムデータ入力(アルバム枚数分)
    for (var i=0; i < json.length; i++) {  
      //タイトル入力
      specTitle[i].innerHTML = json[i].title;
      //発売日入力
      specRelease[i].innerHTML = json[i].release;
      //曲名入力(曲数分ループ)
      for(var t=0; t < json[i].track.length; t++) {
        var list = document.createElement('li');
        list.innerHTML= json[i].track[t];
       specTrack[i].appendChild(list);        
      }
    }
  });
  • Fetch APIを使って外部のjsonファイルをアルバム情報を読み込む
  • DOMをアルバム枚数、アルバム曲数分複製する
  • DOMの中にアルバム情報を入れている

詳細はこちらを参照。

※参考:【JSON】外部JSONファイル化したはっぴいえんどアルバム情報をFetch APIでDOMに読み込む - クモのようにコツコツと

エラーで処理が動かないコード

それに対して、エラーがあって処理が動かないサンプル。

See the Pen fetch api 03_debugSample by イイダリョウ (@i_ryo) on CodePen.

JSの処理がエラーで動かず、元からあったh1タグのテキストしか表示されていない。

JSコード

//jsonファイルURL
var dmain = 'dl.dropboxusercontent.com';
var directory = 's/x6tsr4ah8cfe9ta';
var file = 'happyend';
var url = 'https://' + dmain + '/' + directory + '/' + file + '.json';

//json読み込み
  fetch(url)
    .then(function(response) {
      return respons.json();
    })
    .then(function(json) {

    //外枠アクセス
    var spec = document.querySelector('.spec');

    //テンプレ複製
    //(アルバム枚数-1枚)
    for (var i=1; i < json.length; i++) {
      var clone = spec.firstElementChild.cloneNode(true);
      spec.appendChild(clone);
    }
    
    //タイトル、発売日、曲名アクセス
    var specTitle = document.querySelectorAll('.title');
    var specRelease = document.querySelectorAll('.release');
    var specTrack = document.querySelectorAll('.track');
    
    //アルバムデータ入力(アルバム枚数分)
    for (var i=0; i < json.length; i++) {  
      //タイトル入力
      specTitle[i].innerHTML = json[i].title;
      //発売日入力
      specRelease[i].innerHTML = jsno[i].release;
      //曲名入力(曲数分ループ)
      for(var t=0; t < json[i].track.length; t++) {
        var list = document.createElement('li');
        list.innerHTML= json[i].track[t];
       specTrack[i].appenChild(list);        
      }
    }
  });

さて、どこがエラーになっているのか。ステップ実行しながら探していく。

ステップ実行-1(アルバム情報がまるまる表示されない)

エラーメッセージを調べる

下記のページをChromeで開き…

(全画面表示)
https://cdpn.io/i_ryo/debug/gOavOwB/NQkzYBWDNOQA

デベロッパーツールを開くと、まず気が付くのは右上に赤い「バツ印」のアイコンがあること。 f:id:idr_zz:20200508182519j:plain

アイコンをクリックすると「Console」パネルでエラーメッセージが出ている。 f:id:idr_zz:20200508184518j:plain

Uncaught (in promise) ReferenceError: respons is not defined

「レスポンスが定義されていないからキャッチできないよー」といってる。

これは変数が見つからない時に出るエラーメッセージ。

どこかで定義されていない変数を参照しています。この変数は、定義されている必要があります。

※参考:ReferenceError: "x" is not defined - JavaScript | MDN

エラーメッセージの下にあるファイル名、行数をクリックすると該当する行を教えてくれている。 f:id:idr_zz:20200508182538j:plain

デベロッパーツール を開いて「バツ印」アイコンがあったらまずはそのエラーメッセージを調べるのがデバッグの基本動作となる。

エラーメッセージの一覧

※参考:JavaScript エラーリファレンス - JavaScript | MDN

これで大抵のことは解決できるがエラーメッセージが出ずに原因が分かりにくいエラーもある*1。また、ステップ実行をするとより詳しく処理の状況を調べることができる。

ブレークポイントを貼る

前回の要領でブレークポイントを設定する。

※参考:【デベロッパーツール 】JSコードのデバッグ(中編:ブレークポイント設定編) - クモのようにコツコツと

「Network」パネルでエラーがありそうなあたりの行にブレークポイントを貼る。 f:id:idr_zz:20200508184545j:plain

そしてサイドバーのメニューの「Pause on exceptions」を有効にする。 f:id:idr_zz:20200508182533j:plain

ここに印を入れるとエラーが発生した場所でブレークして、エラー内容を示してくれるので便利!*2

※参考:Chrome DevToolsでjavascriptをデバッグする際に忘れてはいけない大切な事 - Qiita

ステップ実行(はじめの一歩)

さっそくステップを実行!

ブラウザを再度リロードするとブレークポイントを貼った場所で画面がブレークした! f:id:idr_zz:20200508193055j:plain まずは「ステップオーバー*3」ではなく「ステップイン*4」で実行する。

前回のサイドバーのメニューでいう③の下矢印アイコン*5f:id:idr_zz:20200507182910j:plain

※参考:【デベロッパーツール 】JSコードのデバッグ(中編:ブレークポイント設定編) - クモのようにコツコツと

ステップを1つ進める画面が変化した! f:id:idr_zz:20200508193101j:plain

  • ソースのオレンジ色は取得した値
  • サイドバーの「Call Stack」はアクティブな処理の行数*6
  • サイドバーの「Scope」にも取得した値が見れる

ソース部分拡大。変数値がオレンジ色の枠で表示されている!(ボリュームが多いと見にくくなるが) f:id:idr_zz:20200508193112j:plain responseにマウスをドラッグすると…

値がツールチップで出る。こちらの方が見やすい! f:id:idr_zz:20200508193115j:plain ヘッダー情報に「ok(成功)」とか「200」のステータスコードやjsonのURLが帰ってきているのがわかる。

サイドバーの拡大。「Scope」に取得した値が見れる。「Local」がローカル変数なので大抵はここを見る。「response」の三角アイコンを開くとさらに詳細が見れる。 f:id:idr_zz:20200508193119j:plain オブジェクト(「キー」と「値」の連想配列形式)の中身はこのように開閉型になっている。

次のステップを実行(エラー発生!)

「ステップイン」で次の行のステップも実行すると… f:id:idr_zz:20200508193107j:plain 画面がエラーが起きて「Pause on exceptions」が発動した!*7

ソース部分の拡大。先ほどのresponseはツールチップが出るが… f:id:idr_zz:20200508193131j:plain

2番目はマウスを乗せてもツールチップが出ない! f:id:idr_zz:20200508193127j:plain

サイドバーの拡大 f:id:idr_zz:20200508193123j:plain エラーメッセージは「responsなんてありませんよー」と言っている。

ん?待てよ…responsresponse…そうか!responseeが抜けていたんだ!

ということがわかる。

エラー部分を修正

先ほどのエラー部分を修正する。

JSコード

  fetch(url)
    .then(function(response) {
      return response.json(); // 「response」→「response」
    })

ドヤ!

See the Pen fetch api 03_debugSample-2 by イイダリョウ (@i_ryo) on CodePen.

おや、今度ははっぴいえんどの1枚目のアルバムタイトルまでは出た!

しかしまた処理が途中で止まっている。「発売日」から先が表示されない。まだエラーがあるようだ。

ステップ実行-2(発売日が表示されない)

エラーメッセージを調べる

再度デバッグする!下記のページをChromeで開き、

(全画面表示)
※参考:https://cdpn.io/i_ryo/debug/WNQzXbg/GnMnbOJRGwKM

デベロッパーツールを開く。お、またエラーのアイコンが! f:id:idr_zz:20200508201316j:plain

Consoleを見てみる。エラーメッセージがある。 f:id:idr_zz:20200508201321j:plain

Uncaught (in promise) ReferenceError: jsno is not defined

「jsno」が見つからないよーと言っている。「not defined」は先ほどと同じで変数が見つからないというメッセージ。*8

ブレークポイントを貼る

エラーがありそうなあたりにブレークポイントを貼る。 f:id:idr_zz:20200508201326j:plain

画面をリロードすると画面がブレーク! f:id:idr_zz:20200508201331j:plain ブレークした行の前の処理で取得した値が表示されている。

ソースの拡大。オレンジ色で値を取得されてるが見にくい。 f:id:idr_zz:20200508201338j:plain

上の行のjsonにマウスを重ねるとアルバム情報が入っていることがわかる。 f:id:idr_zz:20200508201343j:plain

サイドバーの拡大。ここでもjsonのアルバム情報は見れる。 f:id:idr_zz:20200508201348j:plain

ステップ実行(エラー発生!)

ステップインで問題の行を実行してみる。 f:id:idr_zz:20200508201352j:plain お、エラーになった。

サイドバーの拡大。上の行のjsonには値が入っていて… f:id:idr_zz:20200508201357j:plain

下のspecReleaseにも値が入っているが… f:id:idr_zz:20200508201400j:plain

問題の箇所jsnoはマウスを重ねても値が出ない! f:id:idr_zz:20200508201405j:plain

サイドバーの拡大。 f:id:idr_zz:20200508201410j:plain jsnoなんてないよー、と言っている。

もうおわかりですね。そう、jsonjsnoと打っちまってる。

エラー部分を修正

先ほどの箇所を修正する。

//発売日入力
      specRelease[i].innerHTML = json[i].release; // 「jsno」を「json」に

ドヤ!

See the Pen fetch api 03_debugSample-3 by イイダリョウ (@i_ryo) on CodePen.

ふむぅ、発売日は表示されたがその次の曲名が1曲も表示されないと。。

ステップ実行-3(曲名が表示されない)

エラー箇所を見る

まだエラーがあるぞと。再度調べんと欲す!

(全画面表示)
※参考:CodePen - fetch api 03_debugSample-3

デベロッパーツールを開く。またエラーあり! f:id:idr_zz:20200508205643j:plain

Uncaught (in promise) TypeError: specTrack[i].appenChild is not a function

今度は「TypeError」と。1行丸々見つからないよーと言っとる…

タイプエラーとは何か。

関数でないものを、関数呼び出ししようとした際に発生するエラーです。

※参考:TypeError: "x" is not a function - JavaScript | MDN

ふむふむ、今回は変数ではなく関数に問題がありそう?

ブレークポイントを貼る

この行にブレークポイントを貼ってみるか。 f:id:idr_zz:20200508205649j:plain

ブラウザをリロードすると画面がブレークする。 f:id:idr_zz:20200508205654j:plain ソースやサイドパネルの「Scope」に取得した値が見える。

ソースの拡大画面。 f:id:idr_zz:20200508205659j:plain

一つ上の行の値。jsonにアルバム情報がある。0は1枚目のアルバム。 f:id:idr_zz:20200508205702j:plain

jsoni番目が0、すなわちアルバムの1枚目。 f:id:idr_zz:20200508205705j:plain

trackt番目が0、すなわち1曲目。 f:id:idr_zz:20200508205709j:plain

そしてlistでは1枚目のアルバムの1曲目は「春よ来い」だよーと。 f:id:idr_zz:20200508205713j:plain ふむふむ、ここまでは1曲目の情報が来ているんだな。だが、画面には表示されない。それはなぜか?

ステップ実行(エラー発生)

「ステップイン」で次の行に進むと、エラーで止まる。 f:id:idr_zz:20200508205717j:plain

specTracki番目は0、すなわち1曲目ですよと。 f:id:idr_zz:20200508205721j:plain

そして1曲目のlistは「春よ来いだよー」と。 f:id:idr_zz:20200508205726j:plain この行にも値は来ているんだな。やはり問題は変数ではないんだなー。

サイドバーを拡大。先ほどの「タイプエラー」の表示。関数が見つかりません、とう意味だったね。 f:id:idr_zz:20200508205730j:plain

再びソース。上にあるappendChild()にマウスを重ねると関数を表すツールチップがでるが… f:id:idr_zz:20200508205734j:plain

下のappenChild()にはマウスを重ねてもツールチップが出ない! f:id:idr_zz:20200508205739j:plain

appendChild()appenChild()…そうか、appenddが抜けていたんだ!

ということがわかる。

エラー部分を修正

ということで関数名を修正する。

       specTrack[i].appendChild(list);     // 「appendChild()」を「appenChild()」に

そうすると、見事、全てのアルバム情報が3枚目まで全て表示された!

See the Pen fetch api 03 by イイダリョウ (@i_ryo) on CodePen.

(全画面表示)
※参考:CodePen - fetch api 03

デベロッパーツールにもエラーメッセージがなくなったぞと! f:id:idr_zz:20200509102218j:plain

処理を抜ける方法(ループ・関数、jsファイル)

ステップ実行している中で「あーこの処理は調べたい内容と関係ないので抜けたいなー」という時の対処法。

ループや関数を抜ける(ステップアウト)

関数を抜けたい時はステップ実行メニューの「ステップアウト」を使う。④の上矢印やつね。 f:id:idr_zz:20200507182910j:plain
これを使うと関数の中の残りの処理を全て実行し、関数の外側に抜ける。

これとブレークポイントを組み合わせるとfor文などのループ処理中でも外側に抜けることができる。

例えばこのfor文、アルバムの曲名を10数回ループして表示する。 f:id:idr_zz:20200509091123j:plain

このfor文の一番最後の行にブレークポイントを貼る。 f:id:idr_zz:20200509091128j:plain

「ステップアウト」を実行するとfor文の中の処理が一発で終わる。 f:id:idr_zz:20200509091133j:plain

さらにループの外側にブレークポイントを貼る。 f:id:idr_zz:20200509091139j:plain

「ステップアウト」を実行するfor文の処理が一発で終わる! f:id:idr_zz:20200509091144j:plain

別ファイルのjsのステップ実行を除外する(Blackbox)

ステップ実行していると途中で別のjsファイルに飛ぶ時がある。調べたい処理とは無関係のファイルだったり、あるいは関係しているけどライブラリやフレームワークの本体ファイルだったり。

こうしたファイルは直接自分で打ち替えて改造することはあまりないと思うので、ファイルごとステップ実行から除外したい*9

いわば「ステップオーバー」を関数単位ではなくファイル単位で行いたい。それを実現できる機能が「Blackbox」。

ステップ実行していて、別のjsファイルに移動した場合 f:id:idr_zz:20200509091149j:plain

右クリックして「Blackbox Script」を選択 f:id:idr_zz:20200509091153j:plain

ファイルがBlackboxになったことが表示される(次回からはこのファイルのステップ実行は除外される) f:id:idr_zz:20200509091159j:plain

なお、Blackboxを解除したい場合は再び右クリックして「Stop blackboxing」を選択する f:id:idr_zz:20200509091204j:plain

最後に

ということで、デベロッパーツールのJSデバッグを3回にわたってお送りしました!

自分はアラートやコンソールでデバッグする時代が結構長かったです。ブレークポイントやステップ実行の存在は知っていましたが、項目やメニューがあまり多すぎてどこを見たらいいのかよくわからなかったです。(「処理を止めて何が嬉しいの?」という感じでしたw)

自分なりに調べながら「こういう順番でここを見ていくのか」という使い方がだんだん分かったのでまとめてみました。同じようにブレークポイントのデバッグをやってみたい方の参考になれば幸いです。*10

それではまた!

※参考:前編はこちら
www.i-ryo.com

※参考:中編はこちら
www.i-ryo.com


※参考:Web開発環境の記事まとめ
qiita.com

*1:今回もエラーになる箇所を複数作ったが、最初のエラーの時点で処理が止まっているため、最初のエラーしか検知できていない

*2:逆に付けないとエラーが発生したところでブレークが解除されてしまう

*3:コールバック関数を無視

*4:コールバック関数を含む

*5:調べたい内容と関係ないコールバック関数に飛んでしまう場合は②のステップオーバーを使う

*6:ここから処理を前後に辿ることができる

*7:先ほどの「Pause on exceptions」をオフにしているとこの時点でステップが解除される

*8:この時点で一目瞭然と思うが、今回はステップ実行がテーマなのでこのまま続けるw

*9:処理の結果が帰ってきた状態がから再開

*10:とは言いつつ、コンソールやアラートのデバッグも手軽なのでいまだに簡単な作業では使いますけどねw