Javascriptのデバッグ中にDevToolsに翻弄された話

## TL;DR
### 何の話か
- Javascriptのオブジェクト配列からfilterメソッドで条件に合う要素を抽出する際、callback関数に引数を渡そうとしたらうまく動かなかった
- ブラウザのDevToolsでデバッグしようとしたら、callback関数には引数が渡ってそうにも見えて、原因究明に数時間かかってしまった
### わかったこと
- filterメソッドのcallback関数にアロー関数を設定すると`this`をバインドできない
- scriptタグのtype属性を雑に設定するとよくない
type属性について参考：[HTMLのscriptタグのtype属性について](https://lethediana.sakura.ne.jp/tech/archives/overview-ja/1963/)

## 問題発生時の状況
- Wordpress記事にReactアプリを埋め込むためにcdnでReactを取り込んで開発中、時刻データを含むオブジェクト配列からfilterメソッドで、変数で定義した時刻より前の時刻データを含むオブジェクトの数をカウントしたかったが、できなかった
### 開発環境（ローカルPC）
- `index.html`と`app.js`を用意
（下記の`app.js`は、該当部分だけ抽出し、処理で使う変数を`const`値に変更して記載）
    ```html:index.html
    <html><body>
        <script src="https://unpkg.com/react@latest/umd/react.development.js" crossorigin="anonymous"></script>
        <script src="https://unpkg.com/react-dom@latest/umd/react-dom.development.js"></script>
        <script src="https://unpkg.com/@mui/material@latest/umd/material-ui.development.js" crossorigin="anonymous"></script>
        <script src="https://unpkg.com/babel-standalone@latest/babel.min.js" crossorigin="anonymous"></script>
        
        <script type="text/babel" src="./app.js"></script>
        
        <div id="root"></div>
    
    </body></html>
    ```
    ```javascript:app.js
    const date_a_array = [
        "2014-05",
        "2014-06",
        "2014-07"
    ]
    const date_b = "2014-06"
    
    class App extends React.Component{
    // --- 略
        let count
        
        count = date_a_array.filter(e => (
                        new Date(e) < new Date(this)
                        )
                    , date_b)
        console.log(count)
    // --- 略
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<App />)
    ```
- コンソールへの出力が`1`になってほしいところ、`0`となったので修正を開始

## DevToolsでデバッグ
Google Chromeの開発者ツールでデバッグした
1.  まず、filterメソッドのコールバック関数（`e => (...)`の部分）の中身（`new Date(e) < new Date(this)`）にブレークポイントを設置し、`this`に`date_b`が渡っているか確認したところ、下記のように確認できた
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109639/bbbdbc01-1206-0bd4-3ad8-b49f645754cd.png)
2. 別の場所で下記の処理を実行すると、コンソールに`true`が出力された
    ```javascript
    console.log(new Date("2014-05") < new Date("2014-06"))
    ```
3. `app.js`の該当部分を下記のように修正すると、コンソールに`1`が出力された
    ```javascript
    count = date_a_array.filter(e => (
                        new Date(e) < new Date("2014-06")
                        )
                    , date_b)
        console.log(count)
    ```
4. デバッグツール上では`date_b`の文字列がcallback関数に渡されているのに、処理が思い通りいっていないため、再度調べなおすことに

## 結局何がおかしいのか
調査と実験をいくつかやって、前節の結果の原因を下記2つに絞った
### 原因1: アロー関数へ`this`がバインドされない
> Many array methods take a callback function as an argument.
...
    ```
        method(callbackFn, thisArg)
    ```
...
The thisArg argument (defaults to undefined) will be used as the this value when calling callbackFn.
...
The thisArg argument is irrelevant for any callbackFn defined with an arrow function, as arrow functions don't have their own this binding.
[参考](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#iterative_methods)
- 多くのArrayメソッドでは、callback関数を（第一）引数として持ち、第二引数に渡した値を、callback関数内で`this`として利用できる
- ただし、アロー関数では`this`がバインドされない
- したがって、下記のようにコードを修正しなければならない
    - アロー関数を使わずに`this`を使う
        ```javascript
        count = date_a_array.filter(function(e){
                        new Date(e) < new Date(date_b)
                        }
                    , date_b)
        console.log(count)
        ```
    - アロー関数を使って`this`を使わない
        ```javascript
        count = date_a_array.filter(e => (
                        new Date(e) < new Date(date_b)
                        ))
        console.log(count)
        ```
- これで想定通りのコンソールへ`1`を出力することを確認できた
- しかし、アロー関数では`this`で値を渡せないはずなのに、なぜDevToolsで`this`に値が渡っているように見えたのか

### 原因2: type="text/babel"としてapp.jsを読み込んでいた
- 実験のため、下記のように確認したい該当箇所だけ抽出して、`_app.js`という新しいファイルを作成した
    ```javascript:_app.js
    const date_a_array = [
        "2014-05",
        "2014-06",
        "2014-07"
    ]
    const date_b = "2014-06"
    
    let count
    
    // case 1他はここでは省略
    
    // case 2
    count = date_a_array.filter(e => (
        new Date(e) < new Date(this)
        )
    , date_b)
    
    console.log(count)
    ```
- 次に、`index.html`でscriptを取り込むとき、下記のように2パターンで取り込んで、前節と同様にDevToolsでブレークポイントを作成して`this`の値を調べた
    - パターン1: `type="text/babel"`
    ```html
    <script type="text/babel" src="./_app.js"></script>
    ```
    **結果**：前節と同じ
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109639/ebdeeb3c-b6c7-ca54-9a3e-2cfcec9c2370.png)

    - パターン2: `type`を指定しない
    ```html
    <script src="./_app.js"></script>
    ```
    **結果**：`this`に値がバインドされていない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109639/088ccbc9-2610-f923-beef-fe5799557259.png)
- これらの結果より、最初にDevToolsで値が`this`にバインドされているように見えたのは、type="text/babel"としてapp.jsを読み込んでいたことが原因であると考えられる
- なぜbabelを読み込むとDevToolsでこのような挙動になるのかまでは未調査
（もし心当たりがある方いらっしゃいましたらシェアいただけるとうれしいです）

## 補足・メモ
- 今回のケースでは、DevToolsで`this`がバインドされているように見えても結局動いていないので、filterメソッドのcallback関数へ値を渡すコードの書き方は変えないといけないことに注意
- （Node.jsを使うなど）cdnを使わない場合も同じようになるのかは不明
- 参考：[MIMEについて](https://lethediana.sakura.ne.jp/tech/archives/summary-ja/1964/)

## 所感
- 備忘録もかねてシェアさせていただきました
- babelのDevToolsへの影響についてはっきりさせないと、完全に原因特定したとは言えないが、あたりがつかなさすぎるため断念しました
（追記）
@murasuke様からかなり掘り下げた補足・追加調査をしていただきました。コメント欄参照。