Modules in JavaScript
Modules in JavaScript
~12 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
We already used import/export to split Account and SavingsAccount into their own files – time to cover the ES module system systematically and structure our project cleanly.
Named exports
export const DEFAULT_CATEGORIES = ['Rent', 'Groceries', 'Leisure', 'Salary'];
export function isValidCategory(category) {
return DEFAULT_CATEGORIES.includes(category);
}// Import individually, by exact name:
import { DEFAULT_CATEGORIES, isValidCategory } from './categories.js';
console.log(isValidCategory('Rent')); // trueDefault exports
Each file may have AT MOST ONE default export – typical for a file's "main export", like our Account class:
export default class Account {
constructor(name, startingBalance) {
this.name = name;
this.balance = startingBalance;
}
}// Default import: the name at import time is FREELY chosen (no curly braces!)
import Account from './account.js';
// Equally valid: import CheckingAccount from './account.js';Achtung: In this tutorial and our budget app project, we consistently prefer named exports (like Account in chapter 19) – the import name stays CONSISTENT across the whole project, and editors can track renames more reliably. Default exports are a legitimate, widely used pattern, but this consistency property often makes named exports the more robust choice for larger projects.
Renaming with as
// Rename on export:
export { DEFAULT_CATEGORIES as CATEGORIES };
// Rename on import, e.g. to avoid naming conflicts:
import { DEFAULT_CATEGORIES as AllCategories } from './categories.js';Importing everything as a namespace
import * as Categories from './categories.js';
console.log(Categories.DEFAULT_CATEGORIES);
console.log(Categories.isValidCategory('Rent'));Cleanly structuring the budget app project
With modules, let's finally split up our project, which has so far grown in a single index.js:
Project structure after chapter 21
haushaltsbuch-app/
├── package.json
└── src/
├── index.js
├── account.js
├── savingsAccount.js
└── categories.jsimport { Account } from './account.js';
import { SavingsAccount } from './savingsAccount.js';
import { DEFAULT_CATEGORIES, isValidCategory } from './categories.js';
const checking = new Account('Checking', 1500);
checking.record('January salary', 2400, 'Salary');
const savings = new SavingsAccount('Money Market', 5000, 2.5);
savings.creditYearlyInterest();
console.log(checking.formatBalance());
console.log(savings.formatBalance());
console.log('Valid categories:', DEFAULT_CATEGORIES);
console.log(isValidCategory('Car')); // falseES modules bring two important properties CommonJS (chapter 2) lacks: every file automatically runs in strict mode (relevant to chapter 17's this behavior), and every file has its OWN module scope – top-level variables do NOT automatically "leak" into the global scope, unlike classic <script> tags in a browser.
With that, phase 3 (scope, closures, OOP) is complete! Phase 4 covers asynchrony: iterators, the event loop, promises, and async/await.