Chrome の Custom Object Formatters 機能を使ったオブジェクトのデバッグ

Chrome でなんかしらのオブジェクトの中身を表示するとき `console.log(obj)` という記述をよくしますよね、これは次の画像のように表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/24f7167c-b738-0751-8724-e455e762ada3.png)

Chrome ではこれをオリジナルな形式で表示することができます。
それが`Custom Object Formatters`という機能です。
うまく利用することで、デバッグの役に立つかも（？）しれません。

本記事ではその使い方について簡単に解説します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/78503a26-056c-9e31-eae6-38cac38d226e.png)


# デモ
とりあえず動かして実際にみたほうが早いので、次の URL を開きましょう。
https://howdy39.github.io/study-custom-object-formatters/

デベロッパーツールを開いて Console を表示しましょう。
上のログが通常のログ表示で、下が`Custom Object Formatters`を使ったログ表示なのですが `Custom Object Formatters`は **Console を開いた状態でないと有効にならない** ので同じ表示になってしまいます。
※通常のログ表示が `▶Object` となっているのも Console を開いていない状態でログ出力しているためです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/c7d92cc1-aafb-9a81-29d1-3500db04af9b.png)


また、`Custom Object Formatters`を使うには設定画面で有効にする必要があります。
Chrome デベロッパーツールの設定画面を表示し（F1）、`Enable Custom Formatters`をチェックしましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/2a3d9a9f-9592-ef7e-201a-b4a76947f10d.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/43ef9696-0d0d-4d73-c2ac-e78aed6e7a77.png)


設定画面を閉じたら**デベロッパーツールを開きながら画面をリロードして** `Custom Object Formatters`が効いているかを確認しましょう。
上がよく見るオブジェクトの表示で下が`Custom Object Formatters`を使ったログ表示になります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/00ad83c1-4e4b-7732-b8b1-573354974a51.png)

次のコードが、この画面の JavaScript になります。詳細は後述します。

```js:js部分（説明は後述する）
  let user = {
    id: "howdy39",
    name: "Tatsuya Nakano",
    age: 42,
  };

  (function() {
    const headerStyle = {
      "style": "background-color: #cfc;"
    }
    const borderStyle = {
      "style": "border: 1px solid grey;"
    };
    const tdStyle = {
      "style": "border: 1px solid grey; padding: 10px;"
    };

    const formatter = {
      header: function(x) {
        if (x === user) {
          return ["span", headerStyle, `userId: ${x.id}`];
        } else {
          return null;
        }
      },
      hasBody: function(x) {
        return x === user;
      },
      body: function(user) {
        const tableHeader = [
          "tr", borderStyle,
          ["td", tdStyle, "名前"], ["td", tdStyle, "年齢"]];

        const tr = [
          "tr", borderStyle,
          ["td", tdStyle, user.name], ["td", tdStyle, user.age]
        ];
        return ["table", borderStyle, tableHeader, tr];
      }
    };
    console.log(user);
    window.devtoolsFormatters = [formatter];
    console.log(user);
  }
)();
```


# Custom Formatter Object の作り方
まず`Custom Formatter Object`というのを作る必要があります。
これは名前のとおり、フォーマッターを定義したオブジェクトです。

`Custom Formatter Object` は `header`, `hasBody`, `body` という名前で３つのfunctionをもったオブジェクトです。
そして作成したオブジェクトを `window.devtoolsFormatters` に設定します。

```js:概要
const formatter = {
  header: function() {},
  hasBody: function() {},
  body: function() {},
};
window.devtoolsFormatters = [formatter];
```


それぞれ次のような意味があります。

- header： Console 上の基底となる部分に表示する内容を返す
- hasBody： header を展開できるか（▶を出すか）どうかを返す
- body： 展開した後に表示する内容を返す

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/596b4cc3-0b50-40db-bcd3-4621d943a085.png)



## htmlTemplate
header の説明に行く前に、まず覚えないといけないのが htmlTemplate の作り方です。
htmlTemplate を使って Console に表示する HTML を作成します。

htmlTemplate は JsonML という書き方を使用します。
[JsonML (JSON Markup Language)](http://www.jsonml.org/)

```js
// 書き方
["タグ名", 属性オブジェクト, 子要素1, 子要素2, ・・・];

//　実例1
["td", {"style": "background-color: red"}, "こんにちは"];

// 実例2
const td = ["td", {"style": "background-color: red"}, "こんにちは"];
["tr", "style": "background-color: red", td];
```

ちなみにタグ名に使えるのは次の7つです。
div, span, ol, li, table, tr, td


## header
header は Console 上の基底となる部分に表示する内容です。htmlTemplate を return します。
デフォルトのフォーマットを使う場合は null を返すようします。

筆者が作ったサンプルでは user 変数だった場合に、htmlTemplate を return しています。

```js:header
header: function(x) {
  if (x === user) {
    return ["span", headerStyle, `userId: ${x.id}`];
  } else {
    return null;
  }
},
```


## hasBody
hasBody は header を展開できるかどうかを返します。
筆者が作ったサンプルでは user 変数だった場合に、展開できるようにしています。

```js:hasBody
hasBody: function(x) {
  return x === user;
},
```

## body
body は 展開した後に表示する内容を返します。
筆者が作ったサンプルでは table とその中身を定義した htmlTemplate を作成し return しています。

```js:body
body: function(user) {
  const tableHeader = [
    "tr", borderStyle,
    ["td", tdStyle, "名前"], ["td", tdStyle, "年齢"]];

  const tr = [
    "tr", borderStyle,
    ["td", tdStyle, user.name], ["td", tdStyle, user.age]
  ];
  return ["table", borderStyle, tableHeader, tr];
}
```

# まとめ

1. window.devtoolsFormatters に Formatter を設定する
2. Formatter には header, hasBody, body の3つの function を定義したオブジェクトを設定する
3. header() は htmlTemplate ないし null を返す function
4. hasBody() は 展開できる場合 true を返す function
5. bodyBody() は htmlTemplate を返す function
6. console.log(obj);等で出力するとフォーマットを変えたログ出力ができる

# 参考
- [Custom Object Formatters in Chrome Dev Tools - Google ドキュメント](https://docs.google.com/document/d/1FTascZXT9cxfetuPRT2eXPQKXui4nWFivUnS_335T3U/edit#heading=h.xuvxhsd2bp05)


# あとがき
技術書典5で [Chrome デベロッパーツールを使いこなそう Console編](https://techbookfest.org/event/tbf05/circle/26640001) という本を書きました。
この記事はその本に入れようと思ったけど長くなりすぎたので削ったやつです。（捨てるの勿体無いのでQiitaにあげました）