モジュールスコープとは何か(まずイメージから)
モジュールスコープは、
「そのモジュール(=1ファイル)の中だけで有効な“自分専用の世界”」 のことです。
ES6 モジュール(import / export を使うファイル)は、
それぞれが「自分だけのスコープ(変数の有効範囲)」を持ちます。
// a.js
const secret = "a の中だけで使う";
// b.js
const secret = "b の中だけで使う";
JavaScriptこの2つの secret は、別々のモジュールスコープにいるので、
互いにぶつかりませんし、相手からも見えません。
ここが重要です。
ES6 モジュールでは、ファイルの先頭で書いた変数も関数も、デフォルトでは「グローバル」ではなく「そのモジュールの中だけ」 です。
「勝手に window や global に漏れない」仕組みになっています。
「グローバル汚染しない」モジュールの世界
普通の script とモジュール script の違い
昔の <script>(type=”module” ではない)では、
ファイルのトップレベルで var や function を書くと、グローバルに飛び出していました。
<script>
var x = 1; // window.x として公開される
function hello() {} // window.hello として公開される
</script>
HTMLしかし ES モジュールの場合:
<script type="module">
const x = 1;
function hello() {}
</script>
HTMLこの x や hello は window に乗りません。
その <script type="module"> の中だけで有効です。
ここが重要です。
モジュールは自動的に「自分だけのスコープ」を持ち、グローバルを汚さない。
他のファイルと変数名がかぶっても平気。
外に出したいものだけ export で明示的に出す。
これがモジュールスコープの基本的なメリットです。
モジュールのトップレベル変数は、そのモジュールの中だけ
// math.js(モジュール)
const internalValue = 42;
export function add(a, b) {
return a + b + internalValue;
}
JavaScript// main.js(モジュール)
import { add } from "./math.js";
console.log(add(1, 2)); // 1 + 2 + 42
console.log(internalValue); // エラー:見えない
JavaScriptinternalValue は math.js のモジュールスコープに閉じ込められていて、main.js からは直接触れません。
モジュールスコープと export / import の関係
export しない限り、他のモジュールから見えない
モジュールスコープの中で宣言したものは、export しないかぎり、そのモジュールの中だけで完結します。
// util.js
const secret = "これは util.js の中だけ";
export function show() {
console.log("show が呼ばれました");
}
JavaScript// main.js
import { show } from "./util.js";
show(); // OK
console.log(secret); // エラー:secret は export されていない
JavaScript設計としては、
- モジュールスコープの中に「内部専用の変数・関数」を書く
- 外に見せたいものだけに
exportを付ける
という形になります。
ここが重要です。
モジュールスコープ+export/import の組み合わせで、
「モジュールの中」と「外に見せる窓口」をきれいに分けられる。
これはクラスのプライベートフィールドやメソッドと同じ「カプセル化」の感覚です。
export されたものだけが「モジュールの外向き API」
// user.js
const prefix = "[User]"; // 内部専用
export function createUser(name) {
return { name };
}
export function printUser(user) {
console.log(prefix, user.name);
}
JavaScript// main.js
import { createUser, printUser } from "./user.js";
const u = createUser("Alice");
printUser(u);
// prefix に触ることはできない
// console.log(prefix); // エラー
JavaScriptこのように、「外に使ってほしいもの」= export された関数・値がモジュールの API になり、
それ以外はモジュールスコープの中で閉じた実装になります。
モジュールスコープとトップレベル this / global の違い
モジュールのトップレベルの this は undefined
普通の script では、トップレベルの this は window(ブラウザ)や global(Node)を指していました。
// 非モジュールスクリプトの場合
console.log(this === window); // true になることが多い
JavaScriptしかし ES モジュールでは違います。
// モジュール(type="module" や .mjs 等)
console.log(this); // undefined
JavaScriptこれは、「モジュールは自分だけのスコープで動いている」ためです。
ここが重要です。
モジュール内で「this を使ってグローバルにアクセスする」という発想は捨ててよく、
必要なら明示的に window や globalThis を使う。
モジュールスコープは、暗黙のグローバル参照を減らす方向の設計です。
本当にグローバルに触りたい場合
どうしてもブラウザ全体のグローバルに触りたい場合は window や globalThis を使います。
// main.js(モジュール)
globalThis.appVersion = "1.0.0";
console.log(globalThis.appVersion); // 1.0.0
JavaScriptただし、モジュールの利点(スコープの分離)を活かすためにも、
グローバルに直接書き込むのは最小限にするのが設計としてはおすすめです。
モジュールスコープと「即時関数」との関係
昔は IIFE(即時関数)でスコープを作っていた
ES6 モジュールがない頃、「グローバル汚染を防ぐ」ためにこう書くことが多かったです。
(function () {
const x = 1;
function foo() {
console.log(x);
}
window.foo = foo; // 必要なものだけグローバルへ
})();
JavaScript関数をその場で実行して、その中だけのスコープを作るテクニック(IIFE)です。
モジュールは「自動で IIFE してくれている」ようなもの
ES モジュールでは、これが最初から言語レベルで用意されています。
// module.js
const x = 1;
export function foo() {
console.log(x);
}
JavaScriptこの x と foo はモジュールスコープに閉じ込められて、foo だけが export を通して外に出されます。
ここが重要です。
ES モジュールは「ファイルごとに自動でスコープを作ってくれる IIFE」のようなもの。
開発者は余計なパターンを書かずに済み、export / import で意図をはっきり書けます。
具体例でモジュールスコープに慣れる
例1:同じ名前の変数を別ファイルで使っても衝突しない
// a.js
const count = 1;
export function showA() {
console.log("A の count:", count);
}
JavaScript// b.js
const count = 100;
export function showB() {
console.log("B の count:", count);
}
JavaScript// main.js
import { showA } from "./a.js";
import { showB } from "./b.js";
showA(); // A の count: 1
showB(); // B の count: 100
JavaScripta.js の count と b.js の count は、
それぞれのモジュールスコープにいるので、互いに影響しません。
例2:内部ロジックをモジュールスコープに閉じ込める
// password.js
const SALT = "static-salt";
function hash(text) {
return `hash(${text}+${SALT})`;
}
export function createPasswordHash(rawPassword) {
return hash(rawPassword);
}
export function verifyPassword(rawPassword, hashed) {
return hash(rawPassword) === hashed;
}
JavaScript// main.js
import { createPasswordHash, verifyPassword } from "./password.js";
const hash = createPasswordHash("secret");
console.log(verifyPassword("secret", hash)); // true
// SALT や hash に直接触ることはできない
// console.log(SALT); // エラー
// hash("xxx"); // エラー(import していないし、export もされていない)
JavaScriptSALT や hash はモジュールスコープの中だけで完結していて、
外からは createPasswordHash と verifyPassword という API でしか触れません。
例3:モジュールスコープ内で「状態を持つ」モジュール
// idGenerator.js
let currentId = 0; // モジュールスコープ内の状態
export function nextId() {
currentId++;
return currentId;
}
JavaScript// main.js
import { nextId } from "./idGenerator.js";
console.log(nextId()); // 1
console.log(nextId()); // 2
console.log(nextId()); // 3
JavaScriptcurrentId は idGenerator.js の中だけで増え続ける状態として存在し、
外からは直接いじれません。
ここが重要です。
モジュールスコープをうまく使うと、「グローバル変数っぽい便利さ」と「外から直接触れない安全さ」を両立できる。
このパターンは「シングルトンなサービス」的な設計にもよく使われます。
まとめ
モジュールスコープの核心は、
「ES6 モジュールごとに、自動で“そのファイルだけのスコープ”が用意されていて、export しない限り外から見えない」 という点です。
押さえておきたいポイントを整理すると:
モジュール(import / export を使うファイル)は、ファイル単位で独立したスコープを持つ
トップレベルで宣言した変数・関数は、デフォルトではグローバルではなく「そのモジュール専用」
外に見せたいものだけを export し、それ以外はモジュールスコープの中に隠す
トップレベルの this はグローバルではなく undefined になる(暗黙のグローバル参照を避ける設計)
昔の IIFE(即時関数)のような「わざとスコープを作るテクニック」を、言語レベルで置き換えたものと考えられる
まずは、小さなファイルを 2 つ作って、
それぞれに同じ名前の変数や関数を書いてみてください。
ES6 モジュールとして動かしたとき、
それぞれが自分のモジュールスコープを持っていて、お互いに干渉しないことが体感できるはずです。
