JavaScriptのモジュールを理解する

## はじめに
基本は備忘録
*モジュールの使用方法で`type="module"`を紹介しているが、基本的には`Webpack`等でビルドすることが前提なので本番環境では紹介した方法は用いない*

## モジュール
現代のJSは1つのファイルに全てのコードを記述するのではなく、機能ごとに分割して管理する
この分割jsファイルをモジュールという

## 利点
名前問題の解決(name space)
モジュールを組み合わせて機能を実装できるようになった

**CommonJS(CJS)**
`nodeJS`上でモジュールを管理する仕組み
`require` / `exports`で読み込みや書き出し


**ECMAScriptESM**
ESの仕様に基づくモジュール管理システム
`import` / `export` で読み込みや露出


## CJCとESMの違い
ESM | vs.| CJS
-|-|-|
import/export|-|require/exports
Browser|-|Node.js
.mjs|-|.cjs

## Import / Export
`Import`
モジュールの読み込み

`Export`
モジュールの露出

## ESM読み込み方法
基本は通常のjsファイルと同様だが、`type="module"`を記述必要がある

## モジュール使用方法
```js
// module側の記載
export let publicVal = 0;
export function publicFn() {
  console.log('publicFn called')
}
```

```js
//実行側の記載
import {publicVal, publicFn } from './moduleA.js';
console.log(publicVal);
publicFn()
```

**`import`した際に使用する際の名前を変更する**

`import{元の名前 as 使用したい名前}`
```js
import {publicVal as val, publicFn as fn } from './moduleA.js';
```

## defaultを使用した例

*1つのファイルで一つ？*

```js
// module側
export let publicVal = 0;

//defaultでexportする記述
export default 0かpublicVal;//なんでも良き
```

```js
//実行側の例
import defaultVal, {publicVal as val} from './moduleA.js';
console.log(val);
console.log(defaultVal)
//defaultでexportする場合{}で囲まない。
//名前はimport時に自由に決めることができる
```

## * as オブジェクト名 from...
`* as オブジェクト名 from...`の記法がある
モジュールのexportとdefaultをまとめて記述可

```js
// module側
export let publicVal = 0;
export function publicFn() {
  console.log('publicFn called')
}

export default 0;
```

```js
// 実行側
import * as moduleA from './moduleA.js';
console.log(moduleA)// オブジェクトにmoduleAの変数や関数が格納
console.log(moduleA.default)// 0
```

## プリミティブ型のexport
モジュールでexportしたプリミティブ型の値の変更は****できない****
理由: モジュールのプリミティブ型の値が格納された変数をimportする際は値自体がコピーされる
そのため、データ不整合が起こりエラーが出る
解決策: オブジェクトにすると参照をコピーするため同じオブジェクトを扱う
そのため、値の変更ができる

## モジュールコンテキストとモジュールスコープ

### モジュールコンテキスト
`ESmodule`を使用した場合`global-context`が`module-context`に変わる
**違いが1点だけ**
module-contextでは`this`が使用できない

```js
console.log(this)// 参照不可

//オブジェクトではないのでレキシカルスコープを辿って
//グローバルスコープのthisを参照しようとする
//上記のconsole.logと一緒なのでthisは参照不可
function fn() {
  console.log(this)
}


const obj = {
  fn() {
    console.log(this)
  }
}
//オブジェクトから呼び出したthisは呼び出し元を参照する
obj.fn()// objの中身を参照

console.log(window)//直接参照すればwindowオブジェクトも参照できる
```
モジュールの場合には関数として読んだ場合にはthisがundefinedになる

```js
<script type="module">
    const obj = {
        name: 'tom',
        hello() {
            console.log(this.name);  // このthisなobjになる。
 
            function justFunction() {
                console.log(this);   // このthisはundefinedになる。
            }
            justFunction();
        }
    }
    obj.hello();
</script>
```


### モジュールスコープ

`ESmodule`を使用した場合`script-scope`が`module-scope`に変わる

モジュールの`mosule-scope`で宣言した変数は実行側にファイルを読み込んでいても`export / import`処理をしていない場合利用することはできない。

グローバルオブジェクトは利用できる

```js
// module側
let a = 0;

window.b = 0;
```

```js
// 実行側

// fromを書いていないが、import処理がないなら必要ない
import 'js/moduleA.js'

console.lor(a)// undefined importしないと利用できない
console.log(window.b)// 0 参照できる
```
*windowを通して変数や関数を利用するのはモジュールの設計思想から外れる*
*可能ではあるが、使用は推奨されておらず、import / exportを利用する*

## moduleの特徴
`type="module"`をつけるとデフォルトで`defer`属性が付与される

通常のJSファイルは読み込んだ分実行されるが、`module`は何度読み込んでも最初の一回のみ実行

`import`も同じで何度モジュールを`import`しても実行されるのは最初の一度だけ

`nomodule`を記載するとmoduleが対応していないブラウザでのみ実行される
`<script nomodule>alert('no module')</script>`

`module属性`を付与するとデフォルトで`Strict`モードになる

*moduleは即時関数に似ている*
*一度だけ実行し、露出したものだけ後から何度でも実行可能*

## Strictモード
通常のJavaScriptで許容されている一部の書き方を**制限**する

`module`はデフォルトでStrictモード

## Strictモードの目的

意図しないバグの混入の防止
予約後の確保
コードのセキュア化
など

## Strictモードの有効化
`use strict`
ファイルの先頭、もしくは関数内の先頭行に記述

## 実際の例

**本来ならエラーが出ない記述をエラーにする**

```js
//関数スコープで定義するはずがgl-s
//に定義していてエラーにつながる可能性
function fn() {
  a = 0; // windowオブジェクトに格納
}

fn();
console.log(a)
```
`use strict`をつけると`let`や`const`なしだとエラーを出せる

**予約語を確保**
```js
const implements, interface, package
```
将来JSの仕様変更で使われる可能性のある単語で
変数宣言しようとするとエラーになる

**セキュア(安全な 堅牢な)**
*例としてthis*
通常はglsで定義した`this`は`window`を参照する

`strictモード`の場合`undefined`にして不用意にwindowオブジェクトは閲覧できないようにしてセキュリティを高めている

---
`strictモード`のthisはプリミティブ型の値も保持できる

通常`this`はオブジェクトへの参照を保持している

```js
function fn() {
  //'use strict'

  return this;
}

console.log(fn.call(2)) //2を保持したオブジェクト Number{2}
```
`Strictモード`の場合

```js
function fn() {
  'use strict'

  return this;
}

console.log(fn.call(2)) // 2
```
*他にも挙動の違いはあるが例として上記のような違いがある*
*今後JSのアップデートはstrictモードに合わせる形で行われていくと予想される*

## Strictモードとclassの関係
`class`の`constructor`や`method`内はデフォルトで`strictモード`になっている

## ダイナミックインポート
新しい方法なのでブラウザ対応必須
*非同期でimportすることができる*

```js
// 通常のimport
import { publicVal } from 'ファイル名';

publicFn()
```

```js
// ダイナミックインポート

import('ファイル名')
.then((modules) => {
modules.publicFn();
})
```
**いつ使うのか？**
画面の初期表示時に必要のないコードなどを必要になった時にダイナミックインポートを使って呼び出すことで
非同期で処理することができる
必要のない処理を非同期で行うことによってユーザーをなるべくまたせずに初期表示を行うことができる


**Promiseが返るのでasyncで利用可能**

```js
// async / awaitの書き換え
async function fn() {
  const modules = await import('ファイル名');
  modules.publicFn();
}
```