JavaScriptの酒のつまみになる話

# はじめに
JavaScriptを触っていると少し直感とは反するような挙動をするようなことがあると思います。
今日は直感と反する内容とその対処方法について説明したいと思います。
具体的には「宣言されていない変数への代入はグローバル変数が作成される」「書き換え不可能なグローバル変数への代入が正常終了する」「関数の引数名の重複がある場合に後の引数が勝つ」の3つになります。

# 宣言されていない変数への代入はグローバル変数が作成される
このことを試すために簡単なテストをしたいと思います。
下記の通りテスト用のコードを作成します。階層はすべて同じ場所に置きます。

```html:test.html
<html>
  <head></head>
  <body>
    <div id = "print"></div>
    <script type="text/javascript" src="test1.js"></script>
    <script type="text/javascript" src="test2.js"></script>
  </body>
</html>
```

```js:test1.js
function test() {
   var_test = "vars test" // ポイントはここで定義されていない変数への代入を行っていること
}
test()
```

```js:test2.js
function print_html(){
  document.getElementById("print").innerHTML = "<p>" + var_test +"</p>" // 別のjsにあるvar_testを使用する
}
print_html()
```

直感的には上記のとおりなコーディングを行った場合はエラーとなりそうですが、宣言されていない変数への代入はグローバル変数を作成して代入する挙動なので`print_html`の中で`var_test`を使用することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/524088/969e419f-11ea-1523-721a-7153a1ad03c9.png)

もちろん、`test`の中で`var`や`let`を使用して定義していればグローバル変数が作成されるわけではないのでエラーとなります。

```js:test1.js
function test() {
   var var_test = "vars test" // test1.jsの中で変数定義して代入
}
test()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/524088/846526b7-22e6-4cf3-f809-3cb3583ed969.png)

# 書き換え不可能なグローバル変数への代入が正常終了する
そもそもグローバル変数だったの？？って思う人もいるかもですが、`undefined`等のグローバル変数は値を書き換えることができません。万が一そういった変数に対して値を代入してしまった場合はエラーが起きるわけではなく値の書き換えはされないままとなります。
その検証のために以下のように先ほどの`test2.js`を下記のように変更してみます。(今回の調査にtest1.jsは関係ないです)

```javascript:test2.js
var undefined = "undefined test";

function print_html(){
  document.getElementById("print").innerHTML = "<p>" + undefined +"</p>"
}
print_html()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/524088/664d7362-f43f-f3a9-33f7-c0767efcb7e5.png)

上記の通りグローバル変数の`undefined`に対して値を`undefined test`を代入していますが、画面に表示されているのはundefinedとなっており、変数は更新されていません。

# 関数の引数名の重複がある場合に後の引数が勝つ
関数の引数名が重複している場合は最初の引数は隠されて後で宣言されている引数が展開されます。
その検証のために以下のように先ほどの`test2.js`を下記のように変更してみます。(今回の調査にtest1.jsは関係ないです)


```javascript:test2.js
function print_html(test, test, test2){
  document.getElementById("print").innerHTML = "<p>" + test +"</p>"+"<p>" + test2 +"</p>"
}
print_html('test','update test', 'test2')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/524088/78866ddc-6a6e-7eeb-03f5-4ddb6bb23e98.png)

`print_html`にて`test`が重複しているのですがこれはエラーになることなく1つめの引数はなかったことになり2つ目の値で展開されます。


# 解決方法「Strict モード」
ここまでの問題を見た時点で知っている人であればStrictモードでの解決方法なんだろうなと分かると思います。
Strictモードは関数やファイルの先頭に`'use strict';`を追加することで使用可能となります。

※スコープに関わる話ではあるので本当はもっと詳しく書くべきですが、今回は割愛します。参考にしたページを見ていただければと思います。
Strictモードで実行した場合はここまでの問題はすべてエラーとなって実行できなくなります。

## 宣言されていない変数への代入はグローバル変数が作成される
Strictモードを使用した場合は`test1.js`の時点で`var_test`が未定義の変数への代入のためエラーとなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/524088/3c1973d2-df22-068e-482d-9b6007530f53.png)

## 書き換え不可能なグローバル変数への代入が正常終了する
Strictモードを使用した場合は`undefined`は書き換え不可能なグローバル変数なのでエラーになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/524088/a688a97a-73c5-4079-1bf7-c01d7c4641d4.png)

## 関数の引数名の重複がある場合に後の引数が勝つ
Strictモードを使用すると関数の重複がある時点でエラーとなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/524088/c86348c9-eae0-fd37-e999-c8f85bce7335.png)

# 終わりに
Strictモードの説明についてStrictモードありきでの説明が多く問題の解決策として説明する記事があまりないなと思って今回の記事を書きました。
Strictモードで解決される問題はここに紹介しただけではありませんし、Strictモードを使うとする場合にも気にするべきポイントはたくさんあります。Strictモードを使うにしても使わないにしてもこういったようなJavaScriptを使用する上で気にしたほうが良い問題点を抑えておくことは重要だと思います。
この記事を機にStrictモードやこれらのJavaScriptの挙動についても目を向けていただければなと思います。

# 参考にしたページ
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Strict_mode