【JavaScript】モジュール

# モジュール
JavaScriptは、はじめは小さなものから始まりました。しかし。昨今のフロントエンドではJavaScriptのコードは非常に大きなものになり、グローバル変数の衝突や保守性などの問題がありました。

`node.js`では、ES2015よりも前にモジュールを分割する`require`構文が実装されていたりなどしていましたが、すでに私達はネイティブのJavaScriptでモジュール構文を使用することができます。

# モジュールのルール
JavaScriptとモジュールの世界のルールは少々異なっています。

## モジュール内は常にStrictモード
はじめに、コードは常に[Strictモード](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Strict_mode)で実行されます。
`use strict`構文を追加する必要はありません。

Strictモードは、そもそも下位互換性を維持して既存のアプリケーションが動かなくなることを防ぐための手段でしたが。モージュルは全く新しいコンテキストであるため、モージュルに下位互換性をもたせる必要はありません。

## ルートコンテキストでのthis
もう一つの違いは、ルートコンテキストで`this`が何を指すかです。
通常のJavaScriptでは、グローバルオブジェクト(ブラウザでは`window`)を指しますが、モジュールでのルートコンテキストの`this`は`undefined`です。

# モジュールのexportとimport
モジュールの中に配置された変数や関数はグローバル空間を汚染しません。
モジュールの中の変数や関数を外の世界で利用するためには、`export`構文を使用します。
エクスポートできるものには、`const`、`let`、`var`、関数、クラスがあります。これらは、最上位の階層にある必要があり、例えば関数内で`export`することはできません。
`export`構文は、デフォルトエクスポートと名前付きエクスポートがあります。

## デフォルトエクスポート
デフォルトエクスポートは、**1つのモジュールにつき1つ**しか宣言することができません。

次のように宣言します。

```js
export default function logging() {
  // なにかログを行う
}
```

エクスポートされた変数や関数は、`import`することで使用することができます。

デフォルトエクスポートされたものは、インポート先で自由に名前をつけることができます。

```js
import LogFunc from './src/log'
```
## 名前付きエクスポート
一方、名前付きエクスポートはモジュールの中で0個以上宣言することができます。
デフォルトエクスポートとの構文の違いは、`defult`を取り除いただけです。

```js
export const array = [10, 20, 30, 40, 50]
export const user = {
  name: '鈴木太郎',
  age: 25
}
export const MAX_AGE = 100
```

名前付きエクスポートをインポートしてみます。モジュールのすべてをインポートしたい場合には、`*`を使用します。

```js
import * as Utils from './src/user'
```

しかし、通常は必要が無いものもインポートするのは良い作法とは言えません。
通常は、次のように限定的にインポートをします。

```js
import { user } from 'src/user`
```

もしもインポートする際に、名前が重複してしまうような場合には、別名をつけることができます。

```js
import { user as defaultUser } from 'src/user`
```

エクスポートする際に別名を使用することもできます。

```js
const user = {
  name: '鈴木太郎',
  age: 25
}

export { user as DefaultUser } 
```

# 動的なモジュールの読み込み
ブラウザで利用できるJavaScriptもモジュールは、常に動的にモジュールを読み込みます。つまり、モジュールが必要なときだけ読み込むようにするので、パフォーマンス上の利点があります。

これは`import`を関数として実行してパラメータとしてパスを指定することができます。これは次のように`Promise`を返します。

```js
import('src/user')
    .then(module => {
        console.log(user.name)
    })
```

# ブラウザでモジュールを実行する

実際にモジュールを使用してみます。

フォルダの構成は次のようになっています。

```
index.html
module1.mjs
module2.mjs
```

ネイティブのJavaScriptモジュールは拡張子`mjs`を持つことになります。

> 注意: ネイティブの JavaScript モジュールは、拡張子 .mjs を持つことが重要です。なぜなら、ブラウザーは JavaScript と互換性のある MIME タイプ text/javascript を持つファイルをインポートします。この拡張子を使うことにより、 "The server responded with a non-JavaScript MIME type" のような厳密な MIME タイプのチェックエラーを避けることができます。また、.mjs という拡張子は明確さ (つまり、このファイルはモジュールであり、通常の JavaScript ではないということ) や、他のツールとの相互利用性の観点からもよいことです。Google のさらに詳細なメモも参照してください

[JavaScript モジュール](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Modules)

それぞれのファイルの中身は以下の通りです。

```index.html
<!DOCTYPE html>
<html>
<head>
	<title>module</title>
</head>
<body>
	<script type="module" src="/module1.mjs"></script>
	<script type="module" src="/module2.mjs"></script>
</body>
</html>
```

`<script>`タグに`type="module"`を含めることで、そのスクリプトがモジュールであることを宣言します。

```module1.mjs
export function plus(num1, num2) {
  return num1 + num2
}
```

```module2.mjs
import { plus } from './module1.mjs'

console.log(plus(1, 1))
```

モジュールは次のようの簡単なファイル構成です。
これをローカルでテストしようとするとき、`file://`URLを使ってHTMLファイルを読み込もうとすると、CORSエラーが発生することに注意してください。

<img width="1050" alt="スクリーンショット 2020-04-13 11.28.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496565/29218ef2-92d3-1fb7-b7de-7a950cc96353.png">

VSCode拡張の[LiveServer](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)を使用すると、簡単にローカルサーバーを建てることができます。
モジュールの読み込みに成功していることが確認できました。

<img width="1050" alt="スクリーンショット 2020-04-13 11.30.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496565/8d3396f0-122b-1615-ab24-5d31693ee90b.png">

# 参考
[JavaScript モジュール](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Modules)
[入門JavaScriptプログラミング](https://www.amazon.co.jp/dp/B07M8T65SM/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)