Skip to content

Instantly share code, notes, and snippets.

@alternacrow
Last active July 23, 2023 09:55
Show Gist options
  • Save alternacrow/0a8d8a8e63f14678245f4692bc1adf72 to your computer and use it in GitHub Desktop.
Save alternacrow/0a8d8a8e63f14678245f4692bc1adf72 to your computer and use it in GitHub Desktop.
JavaScript Module System

JavaScript Module System

TL;DR

ES Modules がデファクトスタンダードになりつつある。
最近の環境なら基本的には ES Moduels で問題ない。
一部 CommonJS じゃないと実行できない環境や、ライブラリ開発の場合は対応が必要になることもあるかもしれない。

モジュールシステムとは

元々 JavaScript はウェブページに動きをつけたりするための言語で、ブラウザー上で動作する言語として生まれた。
JavaScript の実行にはインラインスクリプトとして記述するか、スクリプトタグで JavaScript ファイルを読み込む方法があるが、これには以下のような問題があった。

  • グローバルスコープが汚染される
  • script タグの順序が読み込みに影響し、依存関係が複雑になる
  • 複数の JavaScript ファイルの読み込みを JavaScript のみで完結出来ず、HTML と密結合になる

IIFE (即時実行関数式) によってグローバル汚染は防ぐことが出来るが、JQuery のように$というグローバル変数の定義は必要になる。 また、依存関係の問題は解決出来ない。

モジュールシステムとは、JavaScript で他の JavaScript ファイルを読み込むためのシステムのことであり、いくつかの仕様がある。

  • CommonJS
    • ウェブブラウザ以外の環境で実行するために作られたプロジェクト
    • ServerJS → CommonJS
    • Node.js のデフォルト
  • AMD
    • Asynchronous Module Definition
      • 非同期モジュールの仕様
    • CommonJS の仕様の一部だったが独立した
    • WebPack や Browserify で良いやんってなった
  • UMD
    • Universal Module Definition
    • 環境関係なく動作するモジュール仕様
      • if 文で頑張る
  • ES Modules(ECMA Script)
    • European Computer Manufacturers Association
      • 欧州電子計算機工業会
      • TC39 で仕様策定してる
    • Node.js でも v16 からサポート

Node.js の ES Modules サポート

ES Modules と CommonJS の違いと互換性

CommonJS と ES Modules の大きな違いは Top-level await が可能かどうかである。 そのため CommonJS は同期的に、ES Modules は非同期的に読み込まれる。

ES Modules から CommonJS を読み込む

import や require を使って読み込むことが可能。

CommonJS から ES Modules を読み込む

ES Modules は非同期で読み込まれるため、dynamic import を使う。
もちろん、CommonJS は Top-level await に対応していないので、非同期関数内で読み込む必要がある。

const moduleOfES = await import("./module.mjs");

モジュールシステムの指定

package.json に"type": "module"を指定することで.jsファイルは ES Modules として扱われる

デフォルトを ES Modules にした場合は、CommonJS は.cjsの拡張子を使う必要がある。
また、import path は下記のようにファイル名と拡張子を省略出来なくなる。

import add from "./esm/libs/calc.js";

TypeScript で書いている場合も、.ts.tsxではなく.js.jsxで記述する。

デフォルトは"type": "commonjs"で、この場合は ES Modules の拡張子を.mjsにする必要がある。

余談

調べてる途中で面白かったやつ

Top-level await と dynamic import でデッドロックを作る
https://qiita.com/uhyo/items/0e2e9eaa30ec2ff05260#循環参照がある場合-4-dynamic-importでデッドロックを作る

<script type="importmap"> https://developer.mozilla.org/ja/docs/Web/HTML/Element/script/type/importmap

Rails 7.0 で標準になった importmap-rails とは何なのか?
https://zenn.dev/takeyuwebinc/articles/996adfac0d58fb

参考

MDN JavaScript モジュール
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Modules

ECMA Script
https://ja.wikipedia.org/wiki/ECMAScript

JavaScript History
https://www.w3schools.com/js/js_history.asp

IIFE (即時実行関数式)
https://developer.mozilla.org/ja/docs/Glossary/IIFE

JavaScript の Module System の歴史
https://scrapbox.io/tasuwo/JavaScript_%E3%81%AE_Module_System_%E3%81%AE%E6%AD%B4%E5%8F%B2

CommonJS
https://ja.wikipedia.org/wiki/CommonJS

v8 JavaScript モジュール https://v8.dev/features/modules

JavaScript の Module System の歴史 https://scrapbox.io/tasuwo/JavaScript_%E3%81%AE_Module_System_%E3%81%AE%E6%AD%B4%E5%8F%B2

CommonJS と ES Modules についてまとめる https://zenn.dev/yodaka/articles/596f441acf1cf3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment