JavaScript 再入門(その18) モジュール import / export

モジュール import / export

はじめに

モジュール構文 import / export *ES2015 は、モジュールの機能を共有することができます。
開発システムが複雑化するとプログラムを複数に分割して開発することになります。汎用的に使われるクラスや関数やオブジェクトを、プログラムファイルとして保存しておき、複数のプログラムでコードとロジックを共有することで、開発工数の削減や可読性の向上をはかることができます。

例えば、複数のプログラムで共用できるスクリプト、計算モジュール / 表示モジュール / 汎用関数モジュールがあるとします。プログラムA / B / C / D がこのモジュールを利用して、計算や表示や汎用処理などを共用しておけば、スクリプトのコード量が減るのはもちろんのこと、計算ロジックに変更があれば、計算モジュールのロジックを修正すれば、個々のプログラムのスクリプトを変更する必要がなく、メンテナンス性も向上します。

モジュール構文は、モジュールの一部の機能を共有したり動的に共有することもできます。

モジュール機能のエクスポート

モジュール機能を利用するためには、モジュール機能を提供する側は export句でどの機能を提供するか宣言しなければなりません。

今回の記事では、./modules/areaCal.js という正方形と長方形の面積を求めるモジュールを作成しました。

// ./modules/areaCal.js
const square = ( arg ) => { return arg ** 2 ; } // <--- 正方形の面積を求める関数
const rectangle = ( argX , argY ) => { return argX * argY ; } // <--- 長方形の面積を求める関数
export { square , rectangle } ; // <--- square と rectangle 関数をエクスポートします。

項目の前に export句をつけてもかまいません。

// ./modules/areaCal.js
export const square = ( arg ) => { return arg ** 2 ; } // <--- 前に export句がついている。
export const rectangle = ( argX , argY ) => { return argX * argY ; } // <--- 前に export句がついている。

エクスポートする際に別名を使用することもできます。

// ./modules/areaCal.js
const square = ( arg ) => { return arg ** 2 ; }
const rectangle = ( argX , argY ) => { return argX * argY ; }
export {
	square as calcSquare , // <--- square を別名 calcSquare でエクスポートします。
	rectangle as calcRectangle , // <--- rectangle を別名 calcRectangle でエクスポートします。
} ;

モジュール機能のインポート

モジュールから機能をインポートするためには、import句でモジュールの取得先ファイル名と機能を指定します。

<script type='module'> // <--- type を module にする必要があります。
import * from './modules/areaCal.js' ; // <--- モジュールのインポート
console.log( calcSquare(30) ) ; // 900
console.log( calcRectangle( 30 , 40 ) ) ; // 1200
</script>

機能をインポートしたことにより、正方形と長方形の面積が求められました。
インポートは、次のように書くこともできます。

import { calcSquare , calcRectangle } from './modules/areaCal.js' ;

全ての機能をインポートしていますが、必要な機能だけをインポートすることもできます。

import { calcSquare } from './modules/areaCal.js' ;

インポートの際に別名を使用することもできます。

import {
	calcSquare as cSqr , // <--- calcSquare を cSqr としてインポート
	calcRectangle as cRctg , // <--- calcRectangle を cRctg としてインポート
} from './modules/areaCal.js' ;
console.log( cSqr(30) ) ; // 900 <--- 別名で関数を実行
console.log( cRctg( 30 , 40 ) ) ; // 1200 <--- 別名で関数を実行

インポートの際にモジュールをオブジェクトにすることもできます。

import * as Calculate from './modules/areaCal.js' ; // <--- モジュールを Calculate オブジェクトとしてインポート
console.log( Calculate.calcSquare(30) ) ; // 900 <--- オブジェクトのメソッドとして実行
console.log( Calculate.calcRectangle( 30 , 40 ) ) ; // 1200 <--- オブジェクトのメソッドとして実行

クラスを使ったモジュールのエクスポートとインポート

areaCal.js に Calculate クラスを作成してエクスポートします。

// ./modules/areaCal.js
class Calculate { // <--- Calculate クラスを作成して、正方形と長方形の面積求めるメソッドを作成
	square( arg ) { return [ arg ** 2 , this ] ; } ; // <--- 計算結果と this を返します。
	rectangle( argX , argY ) { return argX * argY ; } ;
}
export { Calculate } ; // <--- Calculate クラスをエクスポート
import { Calculate } from './modules/areaCal.js' ; // <--- Calculate クラスをインポート
const calc = new Calculate ;// <--- Calculate クラスのインスタンスを生成
console.log( calc.square(30) ) ; // [900, Calculate] <--- クラスのメソッドとして実行
console.log( calc.rectangle( 30 , 40 ) ) ; // 1200 <--- クラスのメソッドとして実行

動的モジュールのインポート

動的なモジュールのインポートは、最初に読み込んでしまうのではなく、必要が生じたときにのみモジュールをインポートすることができます。

モジュールへのパスをパラメータとして import() を関数として実行することで、動的にモジュールをインポートすることができます。インポート結果は Promise (非同期処理完了の結果)で返されます。

ボタンをクリックした際にモジュールをインポートして計算結果を返します。
メインのHTMLは「JavaScript 再入門(その1) はじめに」で提供している HTML を使用しています。

JavaScript 再入門(その1) はじめに
はじめに EMCAScript 3th edition(ES3)の頃独学ではじめたJavaScriptですが、2015年に公開されたECMAScript 201…
sakura-system.com
<script type='module'>
/* ボタンをクリックすると計算モジュールをインポートして計算し、計算結果がコンソールに返される。 */
window.onload = function() {
	let testBtn = document.querySelector('#test-button') ; //<--- ID が test-button のタグの要素を取得
	testBtn.addEventListener('click', () => { //<--- テストボタンをクリックするとモジュールがインポートされる。
		import('./modules/areaCal.js').then((Module) => { //<--- インポートは Promise で返される。
			const calc = new Module.Calculate ;
			console.log( calc.square(30) ) ; // [900, Calculate]
		}) ;
		console.log('Clicked') ; // Clicked <--- import は非同期でモジュールをインポートするので先に処理される。
	}) ;
}
</script>

モジュール機能の注意点

  1. モジュールを使うにはスクリプトタイプを module にする必要があります。
  2. モジュール内部で定義されたスクリプトの動作は Strict モードで実行されます。
    ルートの this は undefined となります。window ではありません。
  3. HTML ファイルをローカルから file:// URLを使って読み込もうとするとJavaScriptモジュールのセキュリティ要件によって CROS エラーが発生します。
  4. モジュール機能はスクリプトのスコープにインポートされるので、インポートしたスクリプトの内部からしか利用できません。
// ./modules/areaCal.js
const square = ( arg ) => { return [ arg ** 2 , this ] ; } // 計算結果と this を返します。
export { square }
<script type='module'> // <--- type を module に設定する。
import * as Calculate from './modules/areaCal.js' ; // <--- モジュールを Calculate オブジェクトとしてインポート
console.log( Calculate.calcSquare(30) ) ; // [900, undefined] <--- this は undefined
console.log(this); // undefined <--- this は undefined
</script>
<script> // <--- 別のスクリプトを作成
console.log(this) ; // window <--- this は window
console.log( Calculate.square(30) ) ; // Uncaught ReferenceError: Calculate is not defined <--- モジュールの呼び出しに失敗
</script>

参考リンク

MDN 開発者向けのウェブ技術 > モジュール
MDN 開発者向けのウェブ技術 > クラス
MDN 開発者向けのウェブ技術 > Strict モード
MDN 開発者向けのウェブ技術 > 標準組み込みオブジェクト > Promise
MDN 開発者向けのウェブ技術 > Document.querySelector()
MDN 開発者向けのウェブ技術 > EventTarget.addEventListener()
ウェブ開発を学ぶ > イベントへの入門
MDN 開発者向けのウェブ技術 > JavaScript「再」入門