タイトル: 他ファイルの関数読み込み
SEOタイトル: JavaScript モジュール完全ガイド(ES Modules import / export / CommonJS / 名前付き / デフォルト)
| この記事の要点 |
|
JavaScript のモジュールシステム
JavaScript で「他のファイルに書いた関数」を呼び出すにはモジュール機構を使います。現在は次の 2 方式が混在しています。
| 方式 | 構文 | 主な環境 |
|---|---|---|
| ES Modules (ESM) | import / export | ブラウザ・モダン Node.js (推奨) |
| CommonJS (CJS) | require() / module.exports | 従来の Node.js |
新規プロジェクトは ESM が標準。既存の Node.js コードでは CJS が現役です。
ES Modules — export
名前付き export
// math.js
export function add(a, b) {
return a + b;
}
export function sub(a, b) {
return a - b;
}
export const PI = 3.14159;
default export
// greet.js
export default function greet(name) {
return `Hello, ${name}!`;
}
1 ファイルに 1 つだけ。「このモジュールが提供するメイン機能」を表します。
ES Modules — import
// main.js
// 名前付き import
import { add, sub, PI } from './math.js';
console.log(add(2, 3)); // 5
console.log(PI); // 3.14159
// default import (任意の名前を付けられる)
import greet from './greet.js';
console.log(greet('Alice'));
// まとめて import
import * as math from './math.js';
console.log(math.add(1, 2));
// エイリアス
import { add as plus } from './math.js';
console.log(plus(2, 3));
// 副作用だけ実行 (return 値を使わない)
import './setup.js';
CommonJS — module.exports / require
Node.js が伝統的に使ってきた方式。ファイル拡張子 .js でかつ package.json に "type": "module" がない場合の Node.js ではこちらが標準です。
// math.js (CommonJS)
function add(a, b) { return a + b; }
function sub(a, b) { return a - b; }
module.exports = { add, sub, PI: 3.14159 };
// または
exports.add = add;
exports.sub = sub;// main.js (CommonJS)
const { add, sub, PI } = require('./math.js');
console.log(add(2, 3)); // 5
// オブジェクトをまるごと
const math = require('./math.js');
console.log(math.add(1, 2));
ESM と CJS の使い分け
| 観点 | ESM | CJS |
|---|---|---|
| 構文 | import / export | require / module.exports |
| 非同期/同期 | 非同期(ロード時に静的解析) | 同期 |
| 循環参照 | サポート (一部制約あり) | サポート (壊れやすい) |
| Tree Shaking | ○ (未使用 export を削除) | × |
| ブラウザ | ○ (ネイティブ対応) | × (バンドラ必須) |
| __dirname / __filename | 使えない (import.meta.url 経由) | 使える |
Node.js での ESM 有効化
// package.json
{
"name": "myapp",
"type": "module",
"main": "index.js"
}
"type": "module" を入れると、その配下の .js が ESM として扱われます。CJS を一部混ぜたい場合はそのファイルだけ .cjs 拡張子にします。逆に CJS プロジェクト内で 1 つだけ ESM にしたい場合は .mjs。
ブラウザでの ESM
ブラウザの ESM ではパスは相対 / 絶対 URL のみ。'./math.js' や '/lib/math.js' は OK、'math.js' や 'lodash' (bare specifier)は基本不可(importmap 使用時除く)。
動的 import
遅延読み込みや条件分岐で読み込む場合は import() 関数形式を使います(Promise を返す)。
// クリック時に初めて読み込む
button.addEventListener('click', async () => {
const { showModal } = await import('./modal.js');
showModal();
});
// 条件付き
if (user.isAdmin) {
const admin = await import('./adminPanel.js');
admin.render();
}
動的 import はバンドラ (Webpack / Vite / Rollup) でもコード分割のトリガーとして使われます。
FAQ
Q: import と require 混ぜていい?
A: Node.js では制約あり。ESM から CJS は import x from 'cjsmodule' でほぼ動く。CJS から ESM は動的 import() が必要。
Q: __dirname が ESM で使えない
A: import.meta.url を使う。const __dirname = path.dirname(fileURLToPath(import.meta.url));
Q: ファイル拡張子は省略できる?
A: Node.js の ESM は拡張子必須(./math.js)。CJS / バンドラでは省略可。
Q: TypeScript ではどっち?
A: ソース上は import/export で書き、tsconfig.json の module オプションで出力先を選ぶ(ESNext / CommonJS / NodeNext など)。