デベロッパーツールの続きです。JSコードのデバッグ、前回はブレークポイントを設定しました。今回は次の工程、ステップ実行編です。処理をブレークすると何が嬉しいのか、ステップを実行すると何がわかるのか。実際にJSコードをデバッグしながら紐解いていきます。それではいきましょう!
【目次】
- 今回使うサンプル
- ステップ実行-1(アルバム情報がまるまる表示されない)
- ステップ実行-2(発売日が表示されない)
- ステップ実行-3(曲名が表示されない)
- 処理を抜ける方法(ループ・関数、jsファイル)
- 最後に
※参考:【デベロッパーツール 】JSコードのデバッグ(中編:ブレークポイント設定編) - クモのようにコツコツと
※参考:Web開発環境の記事まとめ
qiita.com
今回使うサンプル
エラーが起きていないコード
今回のステップ実行の題材にするサンプルがこちら。
See the Pen fetch api 03 by イイダリョウ (@i_ryo) on CodePen.
「はっぴいえんど」のアルバム情報外部の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で開き…
デベロッパーツールを開くと、まず気が付くのは右上に赤い「バツ印」のアイコンがあること。
アイコンをクリックすると「Console」パネルでエラーメッセージが出ている。
Uncaught (in promise) ReferenceError: respons is not defined
「レスポンスが定義されていないからキャッチできないよー」といってる。
これは変数が見つからない時に出るエラーメッセージ。
どこかで定義されていない変数を参照しています。この変数は、定義されている必要があります。
※参考:ReferenceError: "x" is not defined - JavaScript | MDN
エラーメッセージの下にあるファイル名、行数をクリックすると該当する行を教えてくれている。
デベロッパーツール を開いて「バツ印」アイコンがあったらまずはそのエラーメッセージを調べるのがデバッグの基本動作となる。
エラーメッセージの一覧
※参考:JavaScript エラーリファレンス - JavaScript | MDN
これで大抵のことは解決できるがエラーメッセージが出ずに原因が分かりにくいエラーもある*1。また、ステップ実行をするとより詳しく処理の状況を調べることができる。
ブレークポイントを貼る
前回の要領でブレークポイントを設定する。
※参考:【デベロッパーツール 】JSコードのデバッグ(中編:ブレークポイント設定編) - クモのようにコツコツと
「Network」パネルでエラーがありそうなあたりの行にブレークポイントを貼る。
そしてサイドバーのメニューの「Pause on exceptions」を有効にする。
ここに印を入れるとエラーが発生した場所でブレークして、エラー内容を示してくれるので便利!*2
※参考:Chrome DevToolsでjavascriptをデバッグする際に忘れてはいけない大切な事 - Qiita
ステップ実行(はじめの一歩)
さっそくステップを実行!
ブラウザを再度リロードするとブレークポイントを貼った場所で画面がブレークした! まずは「ステップオーバー*3」ではなく「ステップイン*4」で実行する。
※参考:【デベロッパーツール 】JSコードのデバッグ(中編:ブレークポイント設定編) - クモのようにコツコツと
ステップを1つ進める画面が変化した!
- ソースのオレンジ色は取得した値
- サイドバーの「Call Stack」はアクティブな処理の行数*6
- サイドバーの「Scope」にも取得した値が見れる
ソース部分拡大。変数値がオレンジ色の枠で表示されている!(ボリュームが多いと見にくくなるが)
response
にマウスをドラッグすると…
値がツールチップで出る。こちらの方が見やすい! ヘッダー情報に「ok(成功)」とか「200」のステータスコードやjsonのURLが帰ってきているのがわかる。
サイドバーの拡大。「Scope」に取得した値が見れる。「Local」がローカル変数なので大抵はここを見る。「response」の三角アイコンを開くとさらに詳細が見れる。 オブジェクト(「キー」と「値」の連想配列形式)の中身はこのように開閉型になっている。
次のステップを実行(エラー発生!)
「ステップイン」で次の行のステップも実行すると… 画面がエラーが起きて「Pause on exceptions」が発動した!*7
ソース部分の拡大。先ほどのresponse
はツールチップが出るが…
2番目はマウスを乗せてもツールチップが出ない!
サイドバーの拡大
エラーメッセージは「respons
なんてありませんよー」と言っている。
ん?待てよ…respons
とresponse
…そうか!response
のe
が抜けていたんだ!
ということがわかる。
エラー部分を修正
先ほどのエラー部分を修正する。
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で開き、
デベロッパーツールを開く。お、またエラーのアイコンが!
Consoleを見てみる。エラーメッセージがある。
Uncaught (in promise) ReferenceError: jsno is not defined
「jsno」が見つからないよーと言っている。「not defined」は先ほどと同じで変数が見つからないというメッセージ。*8
ブレークポイントを貼る
エラーがありそうなあたりにブレークポイントを貼る。
画面をリロードすると画面がブレーク! ブレークした行の前の処理で取得した値が表示されている。
ソースの拡大。オレンジ色で値を取得されてるが見にくい。
上の行のjson
にマウスを重ねるとアルバム情報が入っていることがわかる。
サイドバーの拡大。ここでもjson
のアルバム情報は見れる。
ステップ実行(エラー発生!)
ステップインで問題の行を実行してみる。 お、エラーになった。
サイドバーの拡大。上の行のjson
には値が入っていて…
下のspecRelease
にも値が入っているが…
問題の箇所jsno
はマウスを重ねても値が出ない!
サイドバーの拡大。
jsno
なんてないよー、と言っている。
もうおわかりですね。そう、json
をjsno
と打っちまってる。
エラー部分を修正
先ほどの箇所を修正する。
//発売日入力 specRelease[i].innerHTML = json[i].release; // 「jsno」を「json」に
ドヤ!
See the Pen fetch api 03_debugSample-3 by イイダリョウ (@i_ryo) on CodePen.
ふむぅ、発売日は表示されたがその次の曲名が1曲も表示されないと。。
ステップ実行-3(曲名が表示されない)
エラー箇所を見る
まだエラーがあるぞと。再度調べんと欲す!
デベロッパーツールを開く。またエラーあり!
Uncaught (in promise) TypeError: specTrack[i].appenChild is not a function
今度は「TypeError」と。1行丸々見つからないよーと言っとる…
タイプエラーとは何か。
関数でないものを、関数呼び出ししようとした際に発生するエラーです。
※参考:TypeError: "x" is not a function - JavaScript | MDN
ふむふむ、今回は変数ではなく関数に問題がありそう?
ブレークポイントを貼る
この行にブレークポイントを貼ってみるか。
ブラウザをリロードすると画面がブレークする。 ソースやサイドパネルの「Scope」に取得した値が見える。
ソースの拡大画面。
一つ上の行の値。json
にアルバム情報がある。0
は1枚目のアルバム。
json
のi
番目が0
、すなわちアルバムの1枚目。
track
のt
番目が0
、すなわち1曲目。
そしてlist
では1枚目のアルバムの1曲目は「春よ来い」だよーと。
ふむふむ、ここまでは1曲目の情報が来ているんだな。だが、画面には表示されない。それはなぜか?
ステップ実行(エラー発生)
「ステップイン」で次の行に進むと、エラーで止まる。
specTrack
のi
番目は0
、すなわち1曲目ですよと。
そして1曲目のlist
は「春よ来いだよー」と。
この行にも値は来ているんだな。やはり問題は変数ではないんだなー。
サイドバーを拡大。先ほどの「タイプエラー」の表示。関数が見つかりません、とう意味だったね。
再びソース。上にあるappendChild()
にマウスを重ねると関数を表すツールチップがでるが…
下のappenChild()
にはマウスを重ねてもツールチップが出ない!
appendChild()
とappenChild()
…そうか、append
のd
が抜けていたんだ!
ということがわかる。
エラー部分を修正
ということで関数名を修正する。
specTrack[i].appendChild(list); // 「appendChild()」を「appenChild()」に
そうすると、見事、全てのアルバム情報が3枚目まで全て表示された!
See the Pen fetch api 03 by イイダリョウ (@i_ryo) on CodePen.
デベロッパーツールにもエラーメッセージがなくなったぞと!
処理を抜ける方法(ループ・関数、jsファイル)
ステップ実行している中で「あーこの処理は調べたい内容と関係ないので抜けたいなー」という時の対処法。
ループや関数を抜ける(ステップアウト)
関数を抜けたい時はステップ実行メニューの「ステップアウト」を使う。④の上矢印やつね。
これを使うと関数の中の残りの処理を全て実行し、関数の外側に抜ける。
これとブレークポイントを組み合わせるとfor文などのループ処理中でも外側に抜けることができる。
例えばこのfor文、アルバムの曲名を10数回ループして表示する。
このfor文の一番最後の行にブレークポイントを貼る。
「ステップアウト」を実行するとfor文の中の処理が一発で終わる。
さらにループの外側にブレークポイントを貼る。
「ステップアウト」を実行するfor文の処理が一発で終わる!
別ファイルのjsのステップ実行を除外する(Blackbox)
ステップ実行していると途中で別のjsファイルに飛ぶ時がある。調べたい処理とは無関係のファイルだったり、あるいは関係しているけどライブラリやフレームワークの本体ファイルだったり。
こうしたファイルは直接自分で打ち替えて改造することはあまりないと思うので、ファイルごとステップ実行から除外したい*9。
いわば「ステップオーバー」を関数単位ではなくファイル単位で行いたい。それを実現できる機能が「Blackbox」。
ステップ実行していて、別のjsファイルに移動した場合
右クリックして「Blackbox Script」を選択
ファイルがBlackboxになったことが表示される(次回からはこのファイルのステップ実行は除外される)
なお、Blackboxを解除したい場合は再び右クリックして「Stop blackboxing」を選択する
最後に
ということで、デベロッパーツールの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