One File, One Module
JavaScript Modules
Modern JavaScript ships code as **modules** — each file is its own scope, with explicit imports and exports.
What you'll learn
- Understand that each file is a separate scope
- Know how to enable modules in HTML and Node
- Recognize the differences from old-style scripts
A module is a JavaScript file with its own scope. Things you
declare at the top of the file are private to that file — unless you
export them. Other files use import to bring them in.
This is how modern apps split code across many small files.
Module Scope
Top-level const, let, function, and class declarations in a
module file are module-scoped. They don’t leak to the global
scope.
const PI = 3.14159; // visible only inside math.js
export function area(r) {
return PI * r * r; // PI is in scope here
} import { area } from "./math.js";
console.log(area(5)); // 78.54...
// console.log(PI); // ReferenceError — PI is not exported Enabling Modules
In the browser
Use <script type="module">:
<script type="module" src="./app.js"></script>
This:
- Treats the file as a module.
- Defers it (runs after the HTML is parsed).
- Lets it use
importandexport. - Makes it run in strict mode automatically.
In Node.js
Two ways:
- Name the file
.mjs, OR - Set
"type": "module"in yourpackage.json.
Either tells Node to treat .js files as ES modules.
Modules Run In Strict Mode
All modules are automatically in strict mode — even if you don’t
write "use strict". This catches more bugs at parse time. You won’t
have to think about it much.
Modules Run Once
Even if many files import the same module, the module is loaded and
executed only once. All importers see the same exports.
console.log("counter.js loaded");
export let count = 0; If three other files import from counter.js, you’ll see
"counter.js loaded" exactly once.
Top-Level await
Modules support top-level await — you can await at the top of
a module without wrapping it in an async function.
const response = await fetch("/api/data");
export const data = await response.json(); Other modules that import from data.js will wait for its setup to
complete before running.
How This Differs From Old Scripts
| Module | Plain script |
|---|---|
File scope (import/export) | Everything global |
| Strict mode by default | Sloppy unless you opt in |
| Deferred by default | Runs in order, blocking parsing |
this at top is undefined | this at top is window |
| Loaded once across importers | Each <script> runs independently |
Modules are strictly better for any non-trivial app. Use them.
Up Next
The actual syntax — import and export.