console.logだけじゃない、JavaScriptデバッグの基本
多くの人が最初に覚えるデバッグ手段がconsole.logで、そのままずっと使い続ける人も少なくありません。確かに動きますが、実はconsoleオブジェクトには他にも便利なメソッドが山ほどあって、使いこなせばデバッグがぐっと速く、見通しよくなります。さらに、Chrome DevToolsのブレークポイントやコールスタック、ウォッチ式に慣れてくると、logに頼る場面は自然と減っていきます。
まずはひと通り見ていきましょう。
4つとも同じコンソールに出力されますが、ブラウザ側での見た目が違います。warning は黄色の背景、error は赤色+アイコン付きで、どちらもスタックトレースが一緒に表示されます。DevTools のフィルターを使えばレベルごとに表示を絞り込めるので、ログが数百件単位で出るようになってきたときに効いてきます。
複数の値をラクにログ出力する
複数の変数を一度に確認したいとき、文字列に連結するのはやめましょう。別々の引数として渡す、もしくはオブジェクトで包んでラベル付きにするのがおすすめです。
{ user, count } はオブジェクトのショートハンド記法で、変数名がそのままキーになります。コンソールには { user: {...}, count: 3 } と表示されるので、user を展開して中身を確認できます。文字列化しないので、オブジェクトの構造もそのまま保てます。
オブジェクト配列は console.table で見やすく
オブジェクトの配列を console.log で出すと、折りたたまれてごちゃっとした表示になりがちです。そんなときは console.table を使えば、ちゃんとした表として描画してくれます。
ブラウザのコンソールで最初の呼び出しを実行すると、3行すべてがソート可能な列付きで表示されます。2回目の呼び出しでは、name と role の列だけに絞り込まれます。APIのレスポンス、クエリ結果、パースしたCSVなど、表形式のデータを扱うときにはこれだけで作業効率が段違いに上がります。
console.dir と console.log の違い
見た目は似ていますが、DOM要素に対しては挙動がはっきり異なります。
const el = document.querySelector("button");
console.log(el); // HTMLを出力: <button>Click me</button>
console.dir(el); // すべてのプロパティを持つJSオブジェクトビューを出力
log は要素を HTML として整形して表示しますが、dir はオブジェクトとしての中身(全プロパティ、イベントハンドラ、算出済みスタイルへの参照まで)を見せてくれます。要素にどんなメソッドや属性があるのかを調べたいときは、迷わず dir を使いましょう。
console.group で関連するログをまとめる
デバッグ作業が長引くと、コンソールはあっという間にログで埋め尽くされます。そんなときは console.group と console.groupEnd の出番です。関連するメッセージを折りたためるブロックにまとめられます。
各呼び出しごとに名前付きのグループができ、折りたたみ可能です。最初から閉じた状態で表示したいときは console.groupCollapsed を使いましょう。怪しそうなところだけ開いて確認したい、という場面で重宝します。
console.time で処理時間を計測する
ちょっとしたパフォーマンス計測なら、console.time と console.timeEnd の組み合わせがいちばん手軽です。
time と timeEnd のラベルは必ず一致させてください。ラベルを変えれば複数のタイマーを同時に走らせることもできます。「このループ遅くない?」程度の軽いチェックならこれで十分ですが、本格的に計測したいときは DevTools の Performance パネルに切り替えましょう。タイムラインとフレームチャートをまるごと記録してくれます。
console.assert の使い方:問題があるときだけログを出す
console.assert は条件が falsy のときだけ出力されます。問題がないときはコンソールを汚さず、それでいてコード中にサニティチェックをそっと仕込んでおける便利な仕組みです。
常に成り立つべき不変条件(インバリアント)のチェックに向いています。一方、正当に失敗し得るケースには向かないので、その場合は素直にエラーを throw しましょう。
スタックトレースをその場で出す
console.trace は、例外を投げずに現在のコールスタックを出力してくれます。「この関数、一体 どこから 呼ばれてるの?」を追いかけたいときに便利です:
出力は inner → outer → (top level) の順で表示されます。実際のアプリでは、デバッグ中のクリックハンドラが 3 箇所から呼ばれていることに気づくきっかけになる、といった使い方ができます。
debugger 文で実行を止める
JavaScript の実行を一番手軽に止める方法は、たった 1 つのキーワードです。
function computeTotal(items) {
const subtotal = items.reduce((s, i) => s + i.price, 0);
debugger;
return subtotal * 1.08;
}
DevTools を開いた状態なら、debugger; はブレークポイントとして機能します。その行で実行が止まり、デバッガのフル機能が使える状態になります。スコープ内の変数、コールスタック、ステップオーバー/ステップインの操作、そして現在の状態に対して式を評価することもできます。逆に DevTools が閉じているときは、debugger; は何もしません。
console.log に頼るのをやめて、初めて本物のデバッガを使ったときの感動はなかなかのものです。事前に「何を出力するか」を決めなくても、スコープ内の変数がすべて見える。条件分岐をステップ実行して、どちらの枝に入ったかをその場で確認できる。やっかいなバグの修正が、数分から数秒に縮みます。
コミット前に debugger 文は消しておきましょう。あるいはもっといいのは、Sources パネルで行番号をクリックして DevTools から直接ブレークポイントを設定することです。
知っておくと得する DevTools の小技
何年も気づかずに使い続けている人が多い便利機能をいくつか紹介します。
- 条件付きブレークポイント: Sources で行番号を右クリックし、
user.id === 42のような条件を設定できます。条件が真のときだけブレークポイントが発動します。 - ログポイント: 同じメニューの「Add logpoint」から追加できます。コードをいじらず、実行を止めずにメッセージだけを出力します。
- コンソールの
$_: 直前に評価された式の結果を参照できます。何か実行したあと、その結果を$_で拾えます。 $0: Elements パネルで現在選択している要素を指します。$0.textContentでクリックした要素の中身を確認できます。- オブジェクトとして保存: コンソール上の値を右クリックして「Store as global variable」を選ぶと、
temp1、temp2といった名前で保存され、あとから自由にいじれます。 - Network パネル → Copy as fetch: 任意のリクエストを
fetch()の呼び出しに変換してくれます。コンソールに貼って書き換えるだけで再実行できます。
どれも必須ではありません。ただ、体で覚えてしまえば確実に時間を節約してくれます。
リリース前のお掃除
開発中の console.log は害はありませんが、本番環境ではただのノイズです。いくつか習慣にしておくと楽になります。
- lint ルール(ESLint の
no-console)で消し忘れを検出しましょう。warnとerrorは例外扱いにしておくのがおすすめです。 - 冗長なログはチェックで包む:
if (process.env.NODE_ENV !== "production") console.log(...)。 - トレース目的の出力には
console.debugを使いましょう。多くのバンドラやログ集約ツールでフィルタリングできます。 - さらに良いのは、小さなロガーモジュール(あるいは
debugのようなライブラリ)を用意しておくこと。コードを書き換えずにカテゴリ単位でログを切り替えられます。
ログ出力はタダではありません。呼び出すたびに引数がシリアライズされ、バッファに書き込まれます。ホットなループの中に消し忘れたログがあると、パフォーマンスに目に見えて響くこともあります。
次回: 正規表現
文字列を扱うコードのデバッグはこれから何度もやることになりますが、その多くは正規表現が絡みます。JavaScript の中でもっともコンパクトで、もっとも暗号めいた機能ですね。次の章では、JavaScript の正規表現と、それを使うメソッドたちを、やさしいところからひと通り見ていきます。
よくある質問
console.logとconsole.dirは何が違うの?
console.logはブラウザのデフォルト表示で値を出力します。DOM要素を渡すとHTMLとしてレンダリングされるのがポイントです。一方のconsole.dirはJavaScriptオブジェクトとしてのプロパティ一覧を常に表示するので、「マークアップじゃなくて要素の中身(プロパティ)を見たい」ときはこちらを使います。
console.logを使わずにChrome DevToolsでデバッグするには?
Sourcesパネルで対象ファイルを開き、行番号をクリックすればブレークポイントが張れます。その行に到達すると実行が止まるので、変数の中身を覗いたり、ステップ実行したり、コンソールで式を評価したりできます。コード側から止めたいときはdebugger;文を書いておけば、そこで自動的にブレークポイントが発火します。
JavaScriptの処理時間を計測する方法は?
計測したい処理をconsole.time('ラベル名')とconsole.timeEnd('ラベル名')で挟むだけでOKです(ラベルは揃えること)。ミリ秒単位で経過時間が表示されます。もっと細かく分析したいときは、DevToolsのPerformanceパネルでフレームチャートを記録すれば、どの処理に時間が食われているか一目瞭然です。
console.tableはどんなときに使うの?
console.tableは配列やオブジェクトをソート可能な表形式で表示してくれるメソッドです。ネストしたオブジェクトをだらっと出力するより圧倒的に見やすい。特にオブジェクトの配列と相性がよく、各オブジェクトが1行、キーが列になります。第2引数に列名を渡せば、表示する列を絞り込むこともできます。