Using LLMs for efficient learning
Up until now, you've been writing all your code in a single file. This works well for small programs, but as your projects grow, keeping everything in one file becomes difficult to manage and understand. Real-world applications are organised into multiple files, where each file has a specific purpose. Some files might contain utility functions, others might handle data processing, and some might define important constants or configurations. This organisation makes your code easier to read, test, and maintain. ES Modules give you the tools to split your code across multiple files and connect them together in a clean, organised way.
Initially, Node.js used CommonJS, Node.js's module system. You may still encounter this way of using multiple files so we will start with that syntax and then later show the new ES modules syntax. CommonJS uses require() to import code and module.exports to export code:
// mathUtils.js
function addFunction(a, b) {
return a + b;
}
module.exports = { add: addFunction };
To use these functions in another file:
// main.js
const { add } = require('./mathUtils.js');
console.log(add(2, 3)); // 5
<aside> 💡
As you can see, whenever we want to show you how a multiple file example works we will add a comment at the top of the code with the filename that that code is in.
</aside>
Let’s break down the code a bit. In the mathUtils.js file we write module.exports = { add: addfunction }; . This tells the Node system that this file wants to provide an export named add that is defined to be the function addFunction. Although it is not mandatory, we usually provide an object to the export so that if we want to add another export we can just add another property to the object.
In the file we want to use the function in, we can then write const { add } = require('./mathUtils.js');. This says that in the file ./mathUtils.js we want to look in the exported object and grab the add property. Notice that we are using object de-structuring as that is the standard for importing.
<aside> ❗
The ./ is extremely important as it specifies the path to find this file. It indicates a relative path, meaning the file should be in the same directory.. If you are unsure what this means and run into issues, you can refresh your knowledge on paths from week 1: Understanding paths
</aside>
<aside> ⌨️
Hands on: Recreate this example in your own IDE and add a multiply function to the mathUtils.js file. Use that function in your main.js file to multiply two numbers. This one is a little difficult, so if you are stuck have a look at the solution below.
</aside>
Nowadays, ES Modules are the standardised way to work with modules in JavaScript. Overall they offer the same functionality but it has as advantage that it is loaded asynchronously. Why that is an advantage is not important right now, that will be something for later in the course. Instead of require and module.exports, ES modules use the import and export keywords:
// mathUtils.js
export function add(a, b) {
return a + b;
}
To use these functions:
// main.js
import { add } from './mathUtils.js';
console.log(add(2, 3)); // 5
Hopefully you agree with the general consensus and find this syntax easier to read.
<aside> ⌨️
Hands on: Rewrite your example of the previous version with the multiply function to using the ES modules syntax.
</aside>
A cool feature of ES Modules is that we can rename the exported variables in our new file:
import { add as sum, multiply as product } from './mathUtils.js';
console.log(sum(2, 3)); // 5
console.log(product(4, 5)); // 20
The concepts below are not used as much and generally it is advised to avoid doing this, but we felt it is important to show you as later on you may encounter these things. In your own code, only do this if there is no other option!
Until now we have exported in what is called the Named Export way. This is when we specify the name of what we are exporting, but it is also possible to provide a default export. Each file can have only one default export:
// calculator.js
const calculate = {
add: (a, b) => a + b,
multiply: (a, b) => a * b,
};
export default calculate;
// main.js
import calculate from './calculator.js';
console.log(calculate.add(5, 3)); // 8
Notice that we don’t de-structure the object here because we are accessing the default export. This means we can name it whatever we want. The following would also work:
// main.js
import calc from './calculator.js';
console.log(calc.add(5, 3)); // 8
In our main.js file we now call it calc, however this is referring to the default exported function calculate. As this can be confusing it is a best practice to avoid using default exports.
Sometimes you may encounter the following syntax:
// mathUtils.js
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// main.js
import * as math from './mathUtils.js';
console.log(math.add(2, 3));
You can read this as import everything from './mathUtils.js' and name it 'math'. It can be useful if you really want to name the file it is coming from, but it also defeats the purpose of importing as this will always import everything. In the above example, the multiply function is also imported even though it is not being used.
This is a lot of input, but luckily you are going to be using multiple files all the time so it will become second nature in to you in no time. To help summarise all the information, have a look at the following video:
We will skip the first 20 seconds as that information is now out of date.
We will skip the first 20 seconds as that information is now out of date.
'./utils.js' not './utils'./ for files in the same directory or ../ for parent directories| Feature | CommonJS (require) | ES Modules (import/export) |
|---|---|---|
| Import syntax | const x = require('...') |
import x from '...' |
| Export syntax | module.exports = ... |
export ... |
| Loading | Synchronous (loads immediately) | Asynchronous (can optimize loading) |
| File extension | .js(default in Node.js) |
.js(requires"type": "module") or.mjs |
| Standard | Node.js specific | JavaScript standard (works everywhere) |
The HackYourFuture curriculum is licensed under CC BY-NC-SA 4.0 **
