headタグ内におけるcharsetの挙動について

アプリケーション(サーバサイド)エンジニアから駆け出しフロントエンドエンジニアに転向したさむとるです。
今回はブラウザにおける文字コードの解釈・挙動における疑問を調べた結果をまとめました。

# あらまし
[WHATWGの規格書](https://html.spec.whatwg.org/multipage/scripting.html#attr-script-async)によると、
`<script>`タグはデフォルトの場合、htmlのパースを止めてjsのダウンロードと実行を行います。

そのため我々フロントエンジニアはしばし`async`や`defer`を用いたりすることで、速度向上を行おうとします

そこで私は疑問に思ってしまったのです

**`charset="utf-8"`の前にutf-8で記述されている`<script>`を置いてしまった場合、文字化けするのではないか？と**
※locale langageがja(日本語)の場合、デフォルトで文字コードはshift-jisになる->[WHATWGの規格書](https://html.spec.whatwg.org/multipage/parsing.html#determining-the-character-encoding)

# 確認
確認するために下記のコードを適当に用意しました

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <script src="foo.js"></script> <!-- utf-8 -->
    <script src="bar.js"></script> <!-- shift-jis -->
    <meta charset="utf-8">
</head>
<body></body>
</html>
```

```javascript:foo.js
alert('アラート1'); // utf-8
```

```javascript:bar.js
alert('アラート2'); // shift-jis
```

# 結果

文字化けしませんでした

![al1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198723/8436edfc-c2ec-5bf5-1af5-08e1a1b120df.png)
![al2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198723/a497cd9b-94b3-0059-2a4a-4b75f4e137f8.png)

理由としては
>The user agent may wait for more bytes of the resource to be available, either in this step or at any later step in this algorithm. For instance, a user agent might wait 500ms or 1024 bytes, whichever came first. In general preparsing the source to find the encoding improves performance, as it reduces the need to throw away the data structures used when parsing upon finding the encoding information. However, if the user agent delays too long to obtain data to determine the encoding, then the cost of the delay could outweigh any performance improvements from the preparse.

とあり、英語が読めないなりにざっくりと解釈したところ
**1024byte分事前捜査し、そこに文字コードの指定があれば使用する**
感じらしいです。勉強になりました。

# 追加検証
500ms/1024byteよりあとに`<chaeset>`をしたら文字化けするのでは？と思い追加検証を行いました。

chromeは頭が良すぎたのでIEで実行しました。

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <script src="foo.js"></script> <!-- utf-8 -->
    <script src="bar.js"></script> <!-- shift-jis -->
    <script>// byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi</script>
    <script>// byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi</script>
    <script>// byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi byte kasegi</script>
    ...
    <meta charset="utf-8">
</head>
<body></body>
</html>
```

みたいな感じでbyte数稼ぎを行います

## 実行結果
![al3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198723/7cc30e4e-1383-474d-7fcf-cf826d97b84c.png)  ![al4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198723/64be137c-8045-a677-8eee-c0190c863cb5.png)
![al5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198723/ea28f6b7-8f37-9c07-7b88-e857662fb378.png)  ![al6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198723/f7a90176-4f76-77cf-2b02-f6002b6b88c0.png)

`<charset>`が解釈されていない`alart`が実行された後に、再度文字コードが正しく解釈された`alart`が表示され、計4回のアラートが表示されました
実行時のコンソールを確認したところ、2回目の`alart`の後に
![console1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198723/93b9d72a-4035-3ae3-e2cb-7054c94bae17.png)
とあり、パーサーが文字コードを解釈した地点で先頭から再実行を行っていることが判明しました。

# 総括
`<charset>`の前にscriptを仕込むと2重で実行されてしまう可能性があり、
少なからずパフォーマンスに影響が出たり、計測系だったら2重送信されてめんどくさい場合が無きにしも非ずなので
基本何があっても初手`<charset>`したほうがいいかもしれません