4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-11-22

はじめに

基本は備忘録
モジュールの使用方法で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"を記述必要がある

モジュール使用方法

// module側の記載
export let publicVal = 0;
export function publicFn() {
  console.log('publicFn called')
}
//実行側の記載
import {publicVal, publicFn } from './moduleA.js';
console.log(publicVal);
publicFn()

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

import{元の名前 as 使用したい名前}

import {publicVal as val, publicFn as fn } from './moduleA.js';

defaultを使用した例

1つのファイルで一つ?

// module側
export let publicVal = 0;

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

* as オブジェクト名 from...

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

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

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

プリミティブ型のexport

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

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

モジュールコンテキスト

ESmoduleを使用した場合global-contextmodule-contextに変わる
違いが1点だけ
module-contextではthisが使用できない

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になる

<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-scopemodule-scopeに変わる

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

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

// module側
let a = 0;

window.b = 0;
// 実行側

// 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
ファイルの先頭、もしくは関数内の先頭行に記述

実際の例

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

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

fn();
console.log(a)

use strictをつけるとletconstなしだとエラーを出せる

予約語を確保

const implements, interface, package

将来JSの仕様変更で使われる可能性のある単語で
変数宣言しようとするとエラーになる

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

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


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

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

function fn() {
  //'use strict'

  return this;
}

console.log(fn.call(2)) //2を保持したオブジェクト Number{2}

Strictモードの場合

function fn() {
  'use strict'

  return this;
}

console.log(fn.call(2)) // 2

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

Strictモードとclassの関係

classconstructormethod内はデフォルトでstrictモードになっている

ダイナミックインポート

新しい方法なのでブラウザ対応必須
非同期でimportすることができる

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

publicFn()
// ダイナミックインポート

import('ファイル名')
.then((modules) => {
modules.publicFn();
})

いつ使うのか?
画面の初期表示時に必要のないコードなどを必要になった時にダイナミックインポートを使って呼び出すことで
非同期で処理することができる
必要のない処理を非同期で行うことによってユーザーをなるべくまたせずに初期表示を行うことができる

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

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?