Node.jsにおいて、複数のファイルにコードを分割して管理することは、大規模なアプリケーション開発を円滑に進めるための基本です。
このファイル間の連携を支えてきたのが、CommonJS規格に基づいたmodule.exportsという仕組みです。
2026年現在、JavaScriptの標準であるES Modules(import/export)の利用が一般的となりましたが、既存のライブラリや特定の実行環境では依然としてmodule.exportsが重要な役割を果たしています。
本記事では、Node.jsにおけるmodule.exportsの基礎知識から、ES Modulesとの具体的な違い、そして現代の開発における最適な使い分けについて詳しく解説します。
Node.jsにおけるmodule.exportsの役割
Node.jsは、一つのJavaScriptファイルを一つの「モジュール」という単位で扱います。
デフォルトの状態では、ファイル内で定義された変数や関数は、そのファイル内だけで有効な「非公開」の状態になっています。
他のファイルからその機能を利用するためには、開発者が意図的にその機能を「外部へ公開」する必要があります。
この公開の手続きを行うためのオブジェクトが、module.exportsです。
Node.jsは実行時に各モジュールを関数でラップしており、その関数の引数としてmoduleオブジェクトを渡しています。
そのため、私たちは特別な設定なしにmodule.exportsに値を代入して、モジュールの出力を定義できるのです。
CommonJSとモジュールシステム
Node.jsが誕生した当初から採用されているモジュール方式を、CommonJS(CJS)と呼びます。
module.exportsはこのCommonJSの仕様に基づいた機能です。
2026年の現代でも、AWS Lambdaなどのサーバーレス環境や、特定の古いパッケージに依存するプロジェクトでは、この形式が標準的に使われ続けています。
また、Node.jsの内部構造の多くがCommonJSで記述されているため、低レイヤーの挙動を理解する上でも欠かせない知識と言えます。
module.exportsの基本的な書き方
module.exportsを使って値をエクスポートする方法には、大きく分けて「単一のエクスポート」と「複数のエクスポート」の2パターンがあります。
1. 単一の関数やクラスをエクスポートする
一つのファイルが一つの主要な機能を持つ場合、module.exports自体にその機能(関数やクラス)を直接代入します。
これにより、インポート側では任意の名前でその機能をすぐに呼び出すことが可能になります。
// mathUtils.js
const add = (a, b) => a + b;
// module.exportsに直接関数を代入する
module.exports = add;
このファイルを読み込む側(main.js)では、require関数を使用します。
// main.js
const addFunction = require('./mathUtils');
console.log(addFunction(5, 3));
8
2. 複数の機能をオブジェクトとしてエクスポートする
一つのモジュール内で複数の便利な関数を提供したい場合は、オブジェクトのプロパティとして各機能を登録します。
// calculator.js
const multiply = (a, b) => a * b;
const divide = (a, b) => a / b;
// オブジェクト形式で複数の関数をエクスポート
module.exports = {
multiply: multiply,
divide: divide
};
インポート側では、オブジェクトを受け取るか、分割代入を利用して特定の機能だけを取り出します。
// main.js
const { multiply, divide } = require('./calculator');
console.log(multiply(4, 2));
console.log(divide(10, 2));
8
5
exportsとmodule.exportsの決定的な違い
Node.jsを学んでいると、module.exportsの他にexportsという記述を見かけることがあります。
実はexportsは、module.exportsへの参照を保持しているだけのショートカット(エイリアス)です。
この二つの使い分けには重要な注意点があります。
プロパティの追加ならexportsで十分
既存のmodule.exportsオブジェクトに新しい機能を追加する場合、exportsを使っても問題ありません。
// utils.js
exports.sayHello = () => "Hello!";
これは内部的には module.exports.sayHello = ... と同じ動作をしています。
exportsへの直接代入は厳禁
しかし、exportsに直接値を代入してはいけません。
exports = someFunction と書いてしまうと、その変数が指し示す先が module.exports ではなく、新しい別の値に切り替わってしまいます。
Node.jsは最終的に「module.exports」の中身だけを外部へ渡すため、exportsへの直接代入は無視される結果となります。
「常にmodule.exportsを使う」という習慣をつけておけば、このようなミスを確実に防ぐことができます。
ES Modules (import/export) との比較
Node.js v12以降、JavaScriptの標準仕様であるES Modules(ESM)が本格的にサポートされました。
2026年の新規プロジェクトではESMが主流となっていますが、module.exportsとの違いを正しく理解しておくことはトラブルシューティングにおいて重要です。
| 特徴 | CommonJS (module.exports) | ES Modules (export) |
|---|---|---|
| 構文 | require / module.exports | import / export |
| ロードのタイミング | 実行時に動的に読み込む(同期) | 実行前に解析される(静的/非同期) |
| ファイルの拡張子 | .js (デフォルト) / .cjs | .mjs / “type”: “module” 設定時の .js |
| Top-level Await | 使用不可 | 使用可能 |
動作のタイミングの違い
requireによる読み込みは、コードが実行されている最中に行われる「同期的な処理」です。
そのため、条件分岐の中でif (condition) require('moduleA')のように動的なインポートを行うことが容易です。
対してESMのimportは、プログラムが動き出す前に構造が解析されるため、より高度な最適化(ツリーシェイキング)が可能です。
2026年の開発現場では、アプリケーションの起動速度やブラウザとの親和性を重視し、ESMが優先される傾向にあります。
相互運用性について
Node.jsでは、ESMからCJS(module.exportsされたファイル)を読み込むことは可能です。
しかし、逆にCJSからESMをrequireすることはできないという制約があります。
この場合は、非同期のimport()関数を使用する必要があります。
この互換性の複雑さが、2026年になってもmodule.exportsを学び続ける必要がある大きな理由の一つです。
2026年における実務上の注意点
現在のNode.js環境では、プロジェクトのpackage.jsonに "type": "module" と記述することが推奨されています。
この設定がある場合、拡張子が.jsのファイルはすべてES Modulesとして扱われます。
もしそのプロジェクト内でどうしてもmodule.exportsを使いたい場合は、拡張子を.cjsに変更する必要があります。
キャッシュメカニズムの理解
module.exportsで公開されたモジュールは、一度requireされるとNode.js内部でキャッシュされます。
二回目以降の呼び出しでは、ファイルの再読み込みは行われず、キャッシュされたオブジェクトが使い回されます。
これにより、モジュール内で定義されたシングルトン(唯一のインスタンス)をアプリケーション全体で共有できるというメリットがあります。
ただし、テストコードなどでモジュールの状態をリセットしたい場合には、キャッシュのクリアが必要になることを覚えておきましょう。
まとめ
Node.jsのmodule.exportsは、長年にわたりサーバーサイドJavaScriptの屋台骨を支えてきた重要な機能です。
ES Modulesへの移行が進んだ2026年現在においても、CommonJS形式の既存資産を扱う機会は非常に多く、その仕組みを深く理解しておくことはエンジニアとしての必須スキルと言えます。
特にexportsとの参照関係の違いや、ESMとの読み込み挙動の差については、意図しないバグを防ぐためにも正確に把握しておきましょう。
新しいプロジェクトを始める際は、まずES Modulesの採用を検討すべきですが、ライブラリの開発や特定の実行環境ではmodule.exportsを選択するのが適切な場合もあります。
それぞれの特性を正しく理解し、プロジェクトの要件に合わせた最適なモジュールシステムを選択してください。
