ES Modules と CommonJS の違い
ES Modules(ESM)と CommonJS(CJS)は、JavaScript の2つの主要なモジュールシステムです。両者には重要な違いがあり、プロジェクトに応じて適切に使い分ける必要があります。
構文の違い
// ES Modules
import { add } from './math.js';
export const PI = 3.14159;
export default function main() {}// CommonJS
const { add } = require('./math');
exports.PI = 3.14159;
module.exports = function main() {};主要な違いの比較
| 特徴 | ES Modules | CommonJS |
|---|---|---|
| 読み込み | 静的(コンパイル時) | 動的(実行時) |
| 非同期 | 可能 | 同期のみ |
| トップレベル await | 可能 | 不可 |
| ブラウザ対応 | ネイティブ | 不可 |
静的 vs 動的
ES Modules(静的)
import/export はファイルの先頭で静的に解析される。条件分岐内では使用不可。これにより Tree Shaking が可能。
CommonJS(動的)
require() は実行時に評価される。条件分岐内で使用可能。静的解析が難しい。
// ES Modules - これはエラー
if (condition) {
import { func } from './module.js'; // SyntaxError
}
// CommonJS - これは OK
if (condition) {
const { func } = require('./module');
}this の違い
// ES Modules のトップレベル
console.log(this); // undefined
// CommonJS のトップレベル
console.log(this); // module.exports を指すファイル拡張子
// ES Modules(ブラウザ)- 拡張子必須
import { func } from './utils.js';
// CommonJS - 拡張子省略可
const { func } = require('./utils');エクスポートの違い
ES Modules のエクスポート
エクスポートは読み取り専用のバインディング。元のモジュールで値が変わると、インポート側でも反映される。
CommonJS のエクスポート
エクスポートは値のコピー。プリミティブ値は元の変更が反映されない。
// ES Modules - バインディング
// counter.js
export let count = 0;
export function increment() { count++; }
// main.js
import { count, increment } from './counter.js';
console.log(count); // 0
increment();
console.log(count); // 1(反映される)// CommonJS - コピー
// counter.js
let count = 0;
module.exports = {
count,
increment: () => { count++; }
};
// main.js
const counter = require('./counter');
console.log(counter.count); // 0
counter.increment();
console.log(counter.count); // 0(反映されない)循環参照の扱い
両方のシステムで循環参照は可能ですが、挙動が異なります。
相互運用性
Node.js では ESM から CJS を、CJS から ESM を読み込むことができます(一部制限あり)。
// ES Modules から CommonJS を読み込む
import cjsModule from './cjs-module.cjs';
// CommonJS から ES Modules を読み込む(非同期)
const esmModule = await import('./esm-module.mjs');どちらを使うべきか
ES Modules を選ぶ場合
新規プロジェクト、ブラウザで動作させたい、Tree Shaking を活用したい、トップレベル await が必要
CommonJS を選ぶ場合
既存の Node.js プロジェクト、ESM 未対応のライブラリに依存、動的な require が必要
現在のトレンドとしては、ES Modules への移行が進んでいます。新規プロジェクトでは ES Modules を選択することが推奨されます。