Step 1 of 7 · Reading · ~4 min
Learn
Modern Syntax
ES Modules: import and export
Real programs are split across files. A module is a file that decides what it shares and what stays private. By the end of this lesson you can read and write import and export statements, explain when a module's code runs, and describe what a module system does behind the scenes.
A module is private by default
Variables and functions declared in a module are visible only inside it. Anything the module wants to share must be exported; anything another file wants to use must be imported.
Named exports
The names in the braces must match the exported names.
Default export
A module may mark one value as its main export. The importer chooses the name:
Namespace import and re-exports
Dynamic import
import("./big.js") is an expression that loads a module on demand and returns a Promise of its namespace. Use it to load code only when needed. A static import must sit at the top level of the file.
When does module code run?
Four rules explain nearly every module question:
- Evaluated once. The first
importof a file runs its top-level code. Every later import, from any file, reuses the result. A module with a counter in it is therefore shared state, a singleton. - Imports are hoisted. All static imports are resolved before any other code in the file runs, wherever you wrote them.
- Imports are live, read-only views. If the exporting module changes its exported variable, importers see the new value. An importer cannot assign to it.
- Cycles are allowed. If
aimportsbandbimportsa, the second module to run sees the first one's exports as they are so far, possibly still empty. Avoid depending on that.
Modules also run in strict mode, and top-level variables are module-scoped, not global.
The older system: CommonJS
Node began with a different system. A file sets module.exports = {...} and others call require("./file"). Conceptually each file is a function that Node calls with a fresh module object and a require function; whatever the function leaves in module.exports is what require returns. require remembers every file it has loaded, so a second require of the same file returns the same object without running the file again, which is rule 1 above.
ES modules are the standard, and they work in browsers (<script type="module">) and in Node (files ending .mjs, or "type": "module" in package.json). Node treats .js files as CommonJS unless told otherwise, and mixing the two systems is a common source of cryptic errors. Relative paths need the ./ and, in Node, the file extension.
About this exercise
The practice runner executes your file as one CommonJS script, so real import lines cannot run here. Instead you will build the machinery under them: a tiny require that evaluates module functions once and shares their exports.
Common mistakes
- Using braces for a default import, or none for a named one.
- Forgetting
./or the.jsextension in a Node import path. - Trying to assign to an imported name.
- Expecting a module to run again on every import. It runs once.
- Destructuring a CommonJS export,
const { count } = require(...), copies the value at that moment, so later changes inside the module are not seen. ES module imports are live.
Your exercise
Write the loader. The starter defines four module functions (two of them require each other) and prints, for each requested name, the keys it exports. You supply makeRequire.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…