JavaScriptのデバッグTips

JavaScript Advent Calendar 2010 8日目担当のid:os0xです。
JavaScriptネタは案外範囲が広くて色んなネタがあるので、毎回が楽しみですね。
さて、私はデバッグをネタにしたいと思います。テストではなくデバッグです。誰かが書いたコードをメンテナンスしなきゃー、とか。jQueryプラグイン導入しようとしたけど、なんかうまく動かないーみたいなケースのおはなしです。
JavaScriptデバッグは大変なので、多くの方が日々苦労されていると思います。なぜJavaScriptデバッグが大変なのか少し整理してみましょう。

  1. ブラウザ依存
    まず、なんといってもJavaScriptはウェブブラウザ上で実行されるので、環境が一定ではありません。特定の環境だけを対象にJavaScriptを書くことは滅多にありません。PC向けではIE、FirefoxChromeSafariOperaなど、スマートフォンはほぼWebKitFirefoxOperaが少々ですが、WebKitの中でのバージョン違いはPC以上に複雑で環境を揃えにくい分むしろ厄介です。
  2. 積極的にはエラーを出さない
    変数を宣言なしに使えて、しかもそれがグローバル変数になるなど、JavaScriptは思わぬところにバグが潜みやすい言語といえます。読み取り専用のプロパティに代入してもエラーにならない(代入がスルーされるだけ)ほどです。なんとなく動いてしまったコードが後々にバグとなって帰ってきます。

また、デバッグの難しさとは少し違いますが、テストを自動化し難いため、バグが再発しやすい問題もあります。

さて、今回は実際にバグが発生したコードにどう立ち向かうと良いか、という点にスポットを当てていきたいと思います。
まずはバグを大雑把に分けしてみましょう。

シンタックスエラー

JavaScriptの解釈時にエラーとなる、比較的分かりやすいエラーです。最新のブラウザならそれなりに親切なエラーメッセージで、どの行にシンタックスエラーがあるのか教えてくれるはずです。
ただし、IEのみで起きるシンタックスエラーには注意が必要です。IEの行番号は当てにならないことが多い上に、外部ファイルが複数あるときなど、どのファイルでのエラーになっているのかをIEは教えてくれません。大抵はオブジェクトリテラル( {hoge: 2, fuga: 0,} など)に余計なカンマが付いていることが原因です(IE以外のブラウザは余計なカンマを許容してしまいます)。まずカンマをチェックして、1ファイルずつ読み込んでエラーになるファイルを探し出し、さらにコメントアウトの範囲を変えてエラーになっている箇所を特定するなど、根気のいる作業が必要となります。
ただ、これらはJSLintなどのチェックツールで検出することも可能です。他にも幾つかチェック方法はあり、コマンドラインから JavaScript のシンタックスチェックを行う方法 - #生存戦略 、それは - subtechを参考にどうぞ。

実行時の例外

どのブラウザでも動いていない場合

まずはFirebugを入れたFirefoxか、Google ChromeのDevtools(WebInspector)、SafariのWebInspector、OperaのDragonfly*1などでデバッグしてみましょう。特にWebInspectorには例外が起きたところでbreakするオプションがあるので、まずはこれでbreakしてみましょう。
f:id:os0x:20101207231846p:image
breakするとこのようにその時のコールスタック、ローカル変数が確認できます(左下のf:id:os0x:20101207231845p:image この状態がエラーのたびにbreakする状態です。もう一度クリックするとcatchされてない例外のみでbreakするモードに、さらにもう一度クリックするとbreakしないデフォルト状態に戻ります)。
このbreakした状態で、さらにコンソールを開くとその状態のままJavaScriptを実行することもできます。ちなみに、arguments.callee.toString()を実行すると、自分自身の関数を文字列として取得できます。arguments.callee.caller.toString() と、callerで呼び出し元を遡っていくこともできて、複雑に入れ子になった処理や、圧縮されたコードを読むときなどに重宝します。
f:id:os0x:20101207233147p:image
ある程度バグに目星をつけたら、該当する箇所にデバッグ用のconsole.logなどのコードを埋め込んだり、debuggerキーワードなどを埋め込んでみましょう。ソースコードを直接編集するより、条件付きブレークなどを活用するとよりスマートにデバッグできるはずです(Firebugで元のJavaScriptのコードに手を入れずにdebug用のconsole出力を入れる方法 - 文殊堂)。
さて、これらのツールを活用すれば、FirefoxChromeSafariOperaで正常に動作するようにすることはそれほど難しくないと思います(もちろん内容によりますが、そこまで複雑なことを実装することは多くないかなと)。

IE対策

さて、ある意味本題のIEでのデバッグです。これはどうしても経験に頼るしかないところかもしれません。
まず、前述のシンタックスエラーは無くなっているものとすると、IEでもJavaScriptは実行されているはずです。つまり、適当にalertを入れていけば、どこまで実行されているのか調べることができます。適当にalertを散らばらせて、問題の範囲を絞っていけば問題の箇所を見つけることができます。alertは処理が止まるので、(ループなどに注意すれば)案外デバッグしやすいと思います。
ちなみに、出力をコピーしたいときはalertではなくconfirmを使ったり、処理を止めずに手軽に値を確認するにはlocation.hashに値を入れるなどのテクニックがあります。もちろん、Firebug Liteを使うのも良いでしょう。
さて、問題の箇所を見つけたとして、それをどのように修正するかは内容によりけりです。ただ、IE対応のノウハウはウェブ上で積極的に共有されているので、(適切なキーワードがわかれば)調べれば解決策が見つかると思います。
また、IEもIE8から開発者ツールが付属しており、特にIE9の開発者ツールはかなり強力になっています。 特に有用なのがconsole.dirです。console.dirは渡したオブジェクトのプロパティとその値をすべて表示してくれます。
f:id:os0x:20101208001851p:image
windowのほかにも、documentやdocument.bodyなど、console.dirで解析すればすべてのプロパティ・メソッドを一覧できます。この情報は非常に有用で、初めて見るようなメソッドが色々見つかると思います。それらが解決の糸口になるかもしれません。

まとめ

要するに、FirebugやWebInspectorなどのデバッグツールを使いこなせばJavaScriptデバッグはすっごく楽になりますよ、というお話です。手前で恐縮ですが、http://gihyo.jp/dev/feature/01/devtoolsこれでできる! クロスブラウザJavaScript入門:第2回 完全版:ブラウザとデバッグ環境|gihyo.jp … 技術評論社も参照頂ければと思います。
それでは、引き続き JavaScript Advent Calendar をお楽しみください。
Happy Xmas!

*1:[https://dragonfly.opera.com/app/cutting-edge:title=最新のOpera]でかなり良くなってます