JavaScriptにも、モジュール機能(import/export)がやってきました!これまではNode.jsなどを用いてモジュールを扱っていましたが、これからはJSの標準機能として使えます。
対応ブラウザ
ES Modulesに対応しているブラウザは以下の通りです。
- Chrome 61
- Safari 10.3
その他のブラウザは未定です。
ES Modulesの使い方
importする側
ES Moduleのimportを使うには、script要素がtype=”module”である必要があります。スクリプト自体は、インラインでも外部ファイルでもかまいません。
<script type="module" src="main.js"></script>
type=”module”が指定されたスクリプトは、defer属性が付与された場合と同様の扱いになります。オプションとしてasync属性を指定することもできます。また、強制的にStrictモードになります。
type=”module”が指定されたスクリプトの中ではimport文が使えるようになります。import文を利用することで、モジュールの一部オブジェクト(または全て)を読み込むことができます。
// 'module.js'から関数helloをimportする。
import { hello } from './module.js';
// importした関数を実行する。
hello();
import文では、読み込むオブジェクト名と、モジュールファイルへのパスを指定します。この例では、module.jsファイルから関数helloをインポートしてます。オブジェクト名は「{}」で囲む必要があることに注意してください。
モジュールのパスの指定は、「/」や「./」「../」から始まるパスで行われる必要があります。つまり「module.js」ではなく「./module.js」にする必要があるということです。また、パスの他にも、URLで指定することも可能です。
import文では一度に複数のオブジェクトを読み込むこともできます。その場合は、名前をカンマで区切ります。加えて、asを使うことで別の名前をつけることもできます。
import { foo, bar as myName, baz } from './module.js';
この例では、fooとbarとbazをインポートし、barにはmyNameという別名をつけています。
アスタリスク記号を使ってすべてのオブジェクトを一度に読み込むこともできます。この場合は、asを使って任意の名前空間に読み込む必要があります。
// 全てのオブジェクトを'Module'に読み込む。
import * as Module from './module.js';
Module.hello();
この例では、Moduleという名前のオブジェクトに、module.jsがエクスポートするすべてのオブジェクトを一度にインポートしています。
import文には、他にも様々な使い方があります。詳しくはMDNのページをご覧ください。
exportする側
スクリプトファイル内でexport文を使うことで、関数やクラスなどの、任意のオブジェクトを外部へエクスポートすることができます。エクスポートしたオブジェクトは、type=”module”のスクリプトからインポートすることができます。
// 関数をexportする。
export function hello() {
console.log('hello!');
}
この例では、関数helloをエクスポートしています。
単一の要素だけではなく、複数のオブジェクトをエクスポートすることも可能です。エクスポートしたい要素すべてにexportをつけてください。
export function hello() {
console.log('hello!');
}
export function world() {
console.log('world!');
}
また、モジュールひとつにつき一度だけexport defaultが利用できます。export defaultはそのモジュールのデフォルトとなるオブジェクトで、少し異なった方法でインポートすることができるようになります。exportが名前付きエクスポートであることに対し、export defaultは名前無しのエクスポートで、インポートする側で名前をつけることになります。
// 関数をexport defaultする。
export default function() {
console.log('hello!');
}
この例では無名関数をモジュールのデフォルトとしてエクスポートしています。名前無しエクスポートとは言いますが、エクスポートするオブジェクトが無名である必要は特にありません。
export defaultを利用する場合、インポートする側では「{}」は不要で、「{ hello }」ではなく「hello」のように書きます。このとき名前は任意のものを用いることができます。
// module.jsのexport defaultを'hello'という名前で読み込む。
import hello from './module.js';
hello();
また、ひとつのモジュールで、exportとexport defaultを混在して使うこともできます。ただしexport defaultはひとつのみに限ります。
export default function() {
console.log('hello!');
}
export function world() {
console.log('world!');
}
このとき両方を同時にインポートすることもできます。
// module.jsのexport defaultを'hello'という名前で読み込む。
// exportされたworld関数も読み込む。
import hello, { world } from './module.js';
hello();
world();
export文には他にも様々な使い方があります。詳しくはMDNのページをご覧ください。
ES Modulesの使用例
まずは任意のファイル内で必要なオブジェクトをexportして準備します。これをmodule.jsとしてみましょう。
// classをexportしている。
export class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `{x:${this.x}, y:${this.y}}`;
}
}
export class Rectangle {
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
toString() {
return `{x:${this.x}, y:${this.y}, width:${this.width}, height:${this.height}}`;
}
}
この例では2つのクラスをexportしています。これら2つがtype=”module”なスクリプトから利用できるオブジェクトとなります。
次にscriptタグにtype=”module”を指定して、そのスクリプトからimportします。このときスクリプトはインラインでも、外部ファイルでも構いません。今回はインラインで書いてみます。
<script type="module">
// 'module.js'でexportされたclassをimportする。
import { Point, Rectangle } from './module.js';
// importしたクラスを使用する。
const p = new Point(1, 2);
const r = new Rectangle(0, 0, 5, 5);
console.log(p.toString());
console.log(r.toString());
</script>
これを実行してみます。
{x: 1, y: 2}
{x: 0, y: 0, width: 5, height: 5}
きちんと表示されました!