ES module コトハジメ #1 ES moduleの仕様と背景
目次
今回は、ES2015仕様において策定されたES moduleについて、解説します。ES moduleとは、JSファイルから別のJSファイルを読み込む仕組みです。#1では、ES moduleのおおまかな仕様について知り、考えていきます。
- ES moduleについて知りたい方
type="module"
を指定したときの仕様について知りたい方- import先の指定のルールを知りたい方
ES moduleとは
ES modulesは、ES2015仕様において策定された、JavaScriptファイルから別のJavaScriptファイルを読み込む仕組みです。
Node.jsでは、ほかのJSファイルの読み込みはCommonJSの仕様に沿った方法ですでに実現していましたが、ES modulesは標準としてNode.jsとブラウザ両方に対応したモジュールシステムの仕様と位置づけられています。
つまり、Node.jsにおいても、ブラウザにおいても使用することができる仕様です。しかし、実際問題Node.jsでは、CommomJS形式の記述が使用されていることも多いため、最終的にはどちらも理解することが求められます。
しかし、私は、フロントエンドエンジニアとして成長していくためには、まずは、ES moduleを理解することが望ましいと考えています。
ES moduleの例
ブラウザの世界では、JavsScriptファイルを読み込むためにはHTMLファイルの中にタグを記述する必要がありました。しかし、ES moduleが実装されたことで、JavaScriptファイルの中にimport文という新しい構文を記述して、ほかのJavaScriptファイルの内容を読み込むことができるようになりました。読み込まれる側では、これも新しい構文であるexport文を使ってどの値を公開するのかを指定することができます。
上記の内容を踏まえた上で、まず、ES moduleの概要をコード上で理解するために、簡単な使用例を示しながら説明します。
- 読み込まれる側
const sum = (a, b) => { return a + b; }
export default sum;
- 読み込む側
import sum from "./sum"
const sum = sum(1, 2);
console.log(sum); // 結果:3
↑ををHTMLから読み込ませるためには、次のようにタグに対してtype="module"
の属性を指定しながら読み込みを行います。これは、ただのJavaScriptとES modules対応のJavaScriptを区別するために必要な記述です。
<script type="module" src="./main.js"></script>
上記の例のように import
と export
を使用することによってJavaScriptファイル内から他のJavaScriptファイルを読み込むことができるようになります。
従来のscriptタグとの違い
先程も説明したとおりですが、ES moduleを有効にするためには、タグに対してtype="module"
の属性を指定すると、従来の<script>
タグとの挙動が違う部分が4つ存在します。その違いについて説明します。
- strict mode
- トップレベルの変数の参照先がモジュール内のみ
- トップレベルの
this
がundefined
- 遅延評価
従来の<script>タグとの挙動の違いは以上の4つになります。一つ一つ詳細を説明します。
strict mode
ES modulesの中で動いているコードは、すべてデフォルトでuse strict
が効いた状態で実行されます。開発者がuse strict
の記述を行わずとも、これは有効になります。
strict modeについて知りたい方は→こちら
トップレベルの変数の参照先がモジュール内のみ
従来の仕様であれば、トップレベルで宣言した変数は、グローバル変数として、ほかのタグを経由して読み込まれたJSファイル、あるいは<script>
タグ内に直接書かれたコード全体から参照できます。
しかし、ES modulesの中で動くコードの場合は、あるモジュール(JSファイル)内のトップレベルで変数を宣言したとしても、その変数が参照できる範囲はモジュールの中だけになります。
トップレベルの this
は undefined
トップレベルでthis
を使った場合の参照先は、従来ではwindow
だったのに対してES modulesとして動くコードの場合はundefined
となります。window
へのアクセスそのものについては、これまでどおりwindow
を直接指定すれば可能です。
遅延評価
従来はJavaScriptファイルは<body>タグの閉じタグである</body>の直前に記述する必要がありました。しかし、 type="module"
を属性に指定することによってJavaScriptファイルが遅延評価されることになります。
この仕様により、JavaScriptファイルを<head></head>の中に書くことができるようになります。
なぜES moduleが生まれたのか
ここでは、ES moduleという仕様がなぜ生まれたのかについて説明します。
一言でいうと、名前空間(スコープ)の問題を解決するため です。
名前空間(スコープ)とは、簡単に説明すると、プログラミングの世界における「名前の影響範囲はここまで」ということを決める仕様になります。
例えば、従来の<script>タグであれば下記のコードは正常に動きます。
<script> const name = "foo"; </script>
<script> console.log(name); // 結果:foo </script>
しかし、上記のコードが正常に動く、従来のJavaScriptでは、大規模開発などでコードが何千、何万行にもなったときに関数名や変数名が被ってしまうという問題が生じます。
そういった名前空間(スコープ)の問題を解決するためにES moduleという仕様が生まれました。ES moduleを使用することによって、名前空間(スコープ)を分割することができるようになり、命名が被ってしまってエラーが出るということを考えることなく、コードを書けるようになりました。
import指定先のルール
最後にimport文を記述するときのルールについて説明します。
同一ディレクトリであってもそれを明示する
// 同一階層の foo.js を取得 import foo from './foo.js';
// 上の階層の bar.js を取得 import bar from '../bar.js';
// ルートパスの baz.js を取得 import baz from '/baz.js';
↑の例のように、"/ ”が必ず必要となります。
そのため、たとえ同一階層だったとしても下記のコードは無効です。
// 同一階層の foo.js を取得する意図だがエラーとなる
import foo from 'foo.js';
拡張子は省略できない
.js
を省略した場合、URLにリクエストが届くだけで、サーバー上のJSファイルに正しくヒットしません。そのため、.js
は必ず必要となります。
しかし、例外として、webpackを使用していると、 .js
の拡張子を省略してもモジュールを取得することができる。Reactなどを使用している場合は省くことに慣れているため、注意が必要である。
あくまで拡張子を省略しても動くのはwebpackの機能であり、ES moduleの機能ではありません。
まとめ
今回はES moduleの基本的は仕様や説明、ES moduleが生まれた背景などを説明しました。私は、ES moduleについてなんとなく使用していましたが、今回記事を調べてみてES moduleについての理解が深まりました。これを呼んでくれた方もこの記事を呼んで、ES moduleについて、さらに理解を深めてくれたら幸いです。