JavaScript
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
144: Modules with CommonJS and ESM
One of the first things that happens as your project grows is that your main file becomes a nightmare to navigate. You can't keep everything in one place. We need to break things apart, but the "how" depends entirely on whether you're dealing with the legacy CommonJS system (which Node.js used for years) or the modern ECMAScript Modules (ESM) standard used by browsers and newer Node projects.
To show you how this works, let's build a small tax calculator for an e-commerce app. We'll need one file to hold our tax rates and another to handle the actual math.
Splitting logic with CommonJS
First, let's look at CommonJS. You'll see this everywhere in older Node.js codebases. It uses require() to bring in dependencies and module.exports to share them. I'll start by creating a rates.js file.
// rates.js
const taxRates = {
NY: 0.08875,
CA: 0.0725,
TX: 0.0625
};
module.exports = taxRates;
Now, in my app.js, I can pull those rates in. Notice how CommonJS is synchronous—it loads the file the moment it hits that line.
// app.js
const rates = require('./rates.js');
function calculateTotal(price, state) {
const rate = rates[state] || 0;
return price + (price * rate);
}
console.log(`Total for NY: $${calculateTotal(100, 'NY')}`);
Running into the ESM wall
Now, I want to modernize this. I'd rather use import and export because that's the official JavaScript standard and it allows for better "tree-shaking" (removing unused code) during the build process. I'll rewrite the files using ESM syntax.
// rates.js
export const taxRates = {
NY: 0.08875,
CA: 0.0725,
TX: 0.0625
};
// app.js
import { taxRates } from './rates.js';
console.log(taxRates.NY);
I tried to run this with node app.js, and immediately, Node screamed at me: SyntaxError: Cannot use import statement outside a module. I forgot a crucial step. By default, Node treats .js files as CommonJS. To tell Node I'm using ESM, I have two choices: rename my files to .mjs or, more commonly, update my package.json.
I'll go with the package.json route since it's cleaner for larger projects. I just need to add one line:
{
"name": "tax-calculator",
"version": "1.0.0",
"type": "module"
}
Once I added "type": "module", the error vanished. One thing to keep in mind: unlike CommonJS, ESM requires you to include the file extension (like ./rates.js) in the import path. If you leave it off, Node will throw another error. It's a bit annoying, but it's the spec.
Comparing the two approaches
You might be wondering why we even care about CommonJS if ESM is the standard. Honestly, you'll spend a lot of your career maintaining code written before ESM was fully adopted. Here is the cheat sheet I use in my head:
- CommonJS: Uses
requireandmodule.exports. Dynamic (you can put arequireinside anifstatement). Standard for old Node.js. - ESM: Uses
importandexport. Static (imports must be at the top of the file). Standard for Browsers and modern Node.js.
My advice? Always start with ESM for new projects. It's the future, and it makes your code more portable between the server and the browser.
📋 Practical Task
Refactoring the Discount Engine to ESM
You've inherited a small project that uses CommonJS, and your lead engineer wants it migrated to ESM. You have two files:
// discounts.js
const discounts = {
SUMMER_SALE: 0.10,
WINTER_SALE: 0.20
};
module.exports = discounts;
// main.js
const discounts = require('./discounts.js');
console.log('Discount applied:', discounts.SUMMER_SALE);
Your Task:
- Rewrite
discounts.jsto use a named export instead ofmodule.exports. - Rewrite
main.jsto use theimportsyntax to bring in that specific named export. - Describe (in a comment or a text note) the specific change you would need to make to the
package.jsonfile to ensure Node.js runs this code without throwing aSyntaxError.
There are no comments for now.