りゅうじの学習blog

学習したことをアウトプットしていきます。

2022年2月6日 JavaScript (JS Primer) [ES2015] ECMAScriptモジュール

モジュールは、保守性・名前空間・再利用性のために使われます。

  • 保守性: 依存性の高いコードの集合を一箇所にまとめ、それ以外のモジュールへの依存性を減らせます
  • 名前空間: モジュールごとに分かれたスコープがあり、グローバルの名前空間を汚染しません
  • 再利用性: 便利な変数や関数を複数の場所にコピーアンドペーストせず、モジュールとして再利用できます

ECMAScriptモジュールの構文

ECMAScriptモジュールは、export文によって変数や関数などをエクスポートできます。 また、import文を使って別のモジュールからエクスポートされたものをインポートできます。 インポートとエクスポートはそれぞれに - 名前つき - デフォルト という2種類の方法があります。

名前つきエクスポート/インポート

名前つきエクスポート

モジュールごとに複数の変数や関数などをエクスポートできます。 次の例では、foo変数とbar関数をそれぞれ名前つきエクスポートしています。 export文のあとに続けて{}を書き、その中にエクスポートする変数を入れることで、宣言済みの変数を名前つきエクスポートできます。

named-export.js //ファイル名


const foo = "foo";
// 宣言済みのオブジェクトを名前つきエクスポートする
export { foo };

また、名前つきエクスポートではexport文を宣言の前につけると、宣言と同時に名前つきエクスポートできます。

named-export-declare.js // ファイル名

// 宣言と同時に名前つきエクスポートする
export function bar() { };

名前つきインポート

指定したモジュールから名前を指定して選択的にインポートできます。 次の例では my-module.jsから名前つきエクスポートされたオブジェクトの名前を指定して名前つきインポートしています。 import文のあとに続けて{}を書き、その中にインポートしたい名前つきエクスポートの名前を入れます。 複数の値をインポートしたい場合は、それぞれの名前をカンマで区切ります。

my-module.js // ファイル名

export const foo = "foo";
export function bar() { }
named-import.js // ファイル名

// 名前つきエクスポートされたfooとbarをインポートする
import { foo, bar } from "./my-module.js";
console.log(foo); // => "foo"
console.log(bar); // => "bar"

名前つきエクスポート/インポートのエイリアス

名前つきエクスポート/インポートにはエイリアスの仕組みがあります。 エイリアスを使うと、宣言済みの変数を違う名前で名前つきエクスポートできます。エイリアスをつけるには、次のようにasのあとにエクスポートしたい名前を記述します。

asをつけることでエイリアス(別名)で変数を扱う事ができます。

named-export-alias.js // ファイル名

const internalFoo = "foo";
// internalFoo変数をfooとして名前つきエクスポートする
export { internalFoo as foo };

ここまではエクスポートの例でしたが名前つきインポート時も同様にエイリアスが使えます。

named-import-alias.js // ファイル名

// fooとして名前つきエクスポートされた変数をmyFooとしてインポートする
import { foo as myFoo } from "./named-export-alias.js";
console.log(myFoo); // => "foo"

エクスポートする側のファイルで長い変数名になってしまった場合にインポート側のファイルで短い名前にしたいときなどにasを使うと便利ですね。

デフォルトエクスポート/インポート

名前つきは終わりここからはデフォルトです。

デフォルトエクスポート

モジュールごとに1つしかエクスポートできない特殊なエクスポートです。 次の例は、すでに宣言されている変数をデフォルトエクスポートしています。 export default文で、後に続く式の評価結果をデフォルトエクスポートします。

default-export.js // ファイル名

const foo = "foo";
// foo変数の値をデフォルトエクスポートする
export default foo;

export文を宣言の前につけると、宣言と同時にデフォルトエクスポートできます。 以下のように関数やクラスの名前を省略できます。

// 宣言と同時に関数をデフォルトエクスポートする
export default function() {}

変数宣言時では宣言と同時にデフォルトエクスポートする事はできません。 変数宣言はカンマ区切りで複数の変数を定義できてしまうためです。

// 変数宣言と同時にデフォルトエクスポートはできない
export default const foo = "foo", bar = "bar";

デフォルトインポート

指定したモジュールのデフォルトエクスポートに名前をつけてインポートします。 次の例では my-module.jsのデフォルトエクスポートにmyModuleという名前をつけてインポートしています。 import文のあとに任意の名前をつけることで、デフォルトエクスポートをインポートできます。

my-module.js // ファイル名

export default {
    baz: "baz"
};
default-import.js // ファイル名

// デフォルトエクスポートをmyModuleとしてインポートする
import myModule from "./my-module.js";
console.log(myModule); // => { baz: "baz" }

実はデフォルトエクスポートは、defaultという固有の名前による名前つきエクスポートと同じものです。 そのため、名前つきエクスポートでas defaultとエイリアスをつけることでデフォルトエクスポートすることもできます。

エイリアスを使えます。変数fooをエクスポート時にdefaultにしています。

default-export-alias.js // ファイル名

const foo = "foo";
// foo変数の値をデフォルトエクスポートする
export { foo as default };

インポート時も同様にエイリアスを使えます。 ただし、defaultは予約語なので、この方法では必ずas構文を使ってエイリアスをつける必要があります。

default-import-alias.js // ファイル名

// デフォルトエクスポートをmyModuleとしてインポートする
import { default as myModule } from "./my-module.js";
console.log(myModule); // => { baz: "baz" }

デフォルトインポートと名前つきインポートはカンマ区切りで同時に記述できます。

default-import-with-named.js // ファイル名

// myModuleとしてデフォルトインポートし、
// fooを名前つきインポートする
import myModule, { foo } from "./my-module.js";
console.log(foo); // => "foo"
console.log(myModule); // => { baz: "baz" }

ECMAScriptモジュールでは、エクスポートされていないものはインポートできません。 なぜならECMAScriptモジュールはJavaScriptのパース段階で依存関係が解決され、インポートする対象が存在しない場合はパースエラーとなるためです。 デフォルトインポートは、インポート先のモジュールがデフォルトエクスポートをしている必要があります。 同様に名前つきインポートは、インポート先のモジュールが指定した名前つきエクスポートをしている必要があります。

エクスポートされていないものはインポートできないという事を覚えておくと良いでしょう。

参考

JS primer ECMAScriptモジュール