モジュールは一度だけ評価される
ES Modules では、同じモジュールが複数の場所からインポートされても、そのコードは一度だけ実行されます。これは「モジュールの評価は一度きり」という重要な特性です。
基本的な動作
同じモジュールを複数回インポートしても、モジュール内のコードは最初の1回しか実行されません。
// counter.js
console.log('counter.js が読み込まれました');
let count = 0;
export function increment() {
count++;
}
export function getCount() {
return count;
}// a.js
import { increment } from './counter.js';
increment();// b.js
import { getCount } from './counter.js';
console.log(getCount()); // 1// main.js
import './a.js';
import './b.js';
import { getCount } from './counter.js';
// "counter.js が読み込まれました" は1回だけ出力される
console.log(getCount()); // 1シングルトンパターンの実現
この特性により、モジュール単位でシングルトン(単一のインスタンス)が自然に実現できます。
// store.js
console.log('Store initialized');
const state = {
user: null,
items: []
};
export function setState(key, value) {
state[key] = value;
}
export function getState(key) {
return state[key];
}どこからインポートしても、同じ state オブジェクトを共有します。
// userModule.js
import { setState } from './store.js';
setState('user', { name: '田中' });// itemModule.js
import { getState } from './store.js';
console.log(getState('user')); // { name: '田中' }モジュールのキャッシュ
モジュールは一度評価されると、結果がキャッシュされます。
初回インポート時
モジュールを読み込み、コードを実行し、エクスポートをキャッシュする
2回目以降のインポート
キャッシュからエクスポートを取得する(コードは再実行されない)
初期化コードの扱い
モジュールの初期化コードは1回だけ実行されることを利用して、セットアップ処理を行えます。
// config.js
import { readFileSync } from 'fs';
// この処理は1回だけ実行される
console.log('設定ファイルを読み込み中...');
const configData = readFileSync('./config.json', 'utf-8');
export const config = JSON.parse(configData);注意点:実行順序
モジュールの評価順序は、依存関係の順に決まります。
// main.js
console.log('main start');
import './a.js';
import './b.js';
console.log('main end');// a.js
console.log('a.js');
import './c.js';// b.js
console.log('b.js');
import './c.js';// c.js
console.log('c.js');出力順序は以下のようになります。
c.js(a.js の依存として最初に評価)
a.js
b.js(c.js は既に評価済みなので再実行されない)
main start
main end
動的インポートでの挙動
動的インポート import() でも同様に、同じモジュールは一度しか評価されません。
// 何度呼び出しても、counter.js のコードは1回だけ実行される
const module1 = await import('./counter.js');
const module2 = await import('./counter.js');
console.log(module1 === module2); // true(同じモジュールオブジェクト)この「一度だけ評価される」という特性を理解しておくことで、モジュール間での状態共有や初期化処理を効果的に設計できます。