タイトル: 他ファイルの関数読み込み
SEOタイトル: JavaScript モジュール完全ガイド(ES Modules import / export / CommonJS / 名前付き / デフォルト)
この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li>JavaScript で他ファイルの関数を読み込むには <strong>ES Modules (ESM)</strong> または <strong>CommonJS</strong> を使う</li><li>ES Modules: <code>export</code> / <code>import</code> 構文。<strong>ブラウザ / Node.js の標準</strong></li><li>CommonJS: <code>module.exports</code> / <code>require()</code>。<strong>Node.js 旧来の方式</strong>、まだ広く現役</li><li><strong>名前付き export</strong> は複数定義可、<strong>default export</strong> は 1 ファイル 1 つ</li><li>読み込み形式の指定: 拡張子 <code>.mjs</code> / package.json の <code>"type": "module"</code> で ESM、それ以外は CJS (Node)</li></ul></td></tr></table>
<h2>JavaScript のモジュールシステム</h2>
<p>JavaScript で「他のファイルに書いた関数」を呼び出すには<strong>モジュール</strong>機構を使います。現在は次の 2 方式が混在しています。</p>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>方式</th><th>構文</th><th>主な環境</th></tr></thead>
<tbody>
<tr><td><strong>ES Modules (ESM)</strong></td><td><code>import</code> / <code>export</code></td><td>ブラウザ・モダン Node.js (推奨)</td></tr>
<tr><td><strong>CommonJS (CJS)</strong></td><td><code>require()</code> / <code>module.exports</code></td><td>従来の Node.js</td></tr>
</tbody>
</table>
<p>新規プロジェクトは ESM が標準。既存の Node.js コードでは CJS が現役です。</p>
<h2>ES Modules — export</h2>
<h3>名前付き export</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// math.js
export function add(a, b) {
return a + b;
}
export function sub(a, b) {
return a - b;
}
export const PI = 3.14159;</code></pre>
<h3>default export</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// greet.js
export default function greet(name) {
return `Hello, ${name}!`;
}</code></pre>
<p>1 ファイルに 1 つだけ。「このモジュールが提供するメイン機能」を表します。</p>
<h2>ES Modules — import</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// 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';</code></pre>
<h2>CommonJS — module.exports / require</h2>
<p>Node.js が伝統的に使ってきた方式。ファイル拡張子 <code>.js</code> でかつ package.json に <code>"type": "module"</code> がない場合の Node.js では<strong>こちらが標準</strong>です。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// 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;</code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// 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));</code></pre>
<h2>ESM と CJS の使い分け</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>観点</th><th>ESM</th><th>CJS</th></tr></thead>
<tbody>
<tr><td>構文</td><td><code>import / export</code></td><td><code>require / module.exports</code></td></tr>
<tr><td>非同期/同期</td><td>非同期(ロード時に静的解析)</td><td>同期</td></tr>
<tr><td>循環参照</td><td>サポート (一部制約あり)</td><td>サポート (壊れやすい)</td></tr>
<tr><td>Tree Shaking</td><td>○ (未使用 export を削除)</td><td>×</td></tr>
<tr><td>ブラウザ</td><td>○ (ネイティブ対応)</td><td>× (バンドラ必須)</td></tr>
<tr><td>__dirname / __filename</td><td>使えない (import.meta.url 経由)</td><td>使える</td></tr>
</tbody>
</table>
<h2>Node.js での ESM 有効化</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-json">// package.json
{
"name": "myapp",
"type": "module",
"main": "index.js"
}</code></pre>
<p><code>"type": "module"</code> を入れると、その配下の <code>.js</code> が ESM として扱われます。CJS を一部混ぜたい場合は<strong>そのファイルだけ <code>.cjs</code> 拡張子</strong>にします。逆に CJS プロジェクト内で 1 つだけ ESM にしたい場合は <code>.mjs</code>。</p>
<h2>ブラウザでの ESM</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!-- type="module" を付けるだけで ESM になる -->
<script type="module" src="./main.js"></script>
<!-- インラインでも OK -->
<script type="module">
import { add } from './math.js';
console.log(add(1, 2));
</script></code></pre>
<p>ブラウザの ESM では<strong>パスは相対 / 絶対 URL のみ</strong>。<code>'./math.js'</code> や <code>'/lib/math.js'</code> は OK、<code>'math.js'</code> や <code>'lodash'</code> (bare specifier)は基本不可(importmap 使用時除く)。</p>
<h2>動的 import</h2>
<p><strong>遅延読み込み</strong>や条件分岐で読み込む場合は <code>import()</code> 関数形式を使います(Promise を返す)。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// クリック時に初めて読み込む
button.addEventListener('click', async () => {
const { showModal } = await import('./modal.js');
showModal();
});
// 条件付き
if (user.isAdmin) {
const admin = await import('./adminPanel.js');
admin.render();
}</code></pre>
<p>動的 import はバンドラ (Webpack / Vite / Rollup) でも<strong>コード分割</strong>のトリガーとして使われます。</p>
<h2>FAQ</h2>
<p><strong>Q: <code>import</code> と <code>require</code> 混ぜていい?</strong><br />
A: Node.js では制約あり。ESM から CJS は <code>import x from 'cjsmodule'</code> でほぼ動く。CJS から ESM は<strong>動的 import()</strong> が必要。</p>
<p><strong>Q: <code>__dirname</code> が ESM で使えない</strong><br />
A: <code>import.meta.url</code> を使う。<code>const __dirname = path.dirname(fileURLToPath(import.meta.url));</code></p>
<p><strong>Q: ファイル拡張子は省略できる?</strong><br />
A: Node.js の ESM は<strong>拡張子必須</strong>(<code>./math.js</code>)。CJS / バンドラでは省略可。</p>
<p><strong>Q: TypeScript ではどっち?</strong><br />
A: ソース上は <code>import/export</code> で書き、<code>tsconfig.json</code> の <code>module</code> オプションで出力先を選ぶ(ESNext / CommonJS / NodeNext など)。</p>