A Guide to JavaScript Modules
JavaScript modules allow you to create multiple interconnected scripts that can import and export data and functionality to each other. When connected to each other in this way, connected .js
files are known as modules.
There are several benefits to using JavaScript modules:
- Better code organization
- Efficiency gains from only loading relevant modules/parts of modules
- Easy import of others’ code and sharing of your own
The organizational and efficiency gains are greatest for large apps, where, given a large code base, only necessary elements are loaded.
Another great benefit is that you can easily import third-party libraries or parts of them for use in your project.
In this tutorial, we’ll look at how you can do all of this, using a conventional example of a parent and child module, in which the parent imports data and functionality from the child module and a third-party library.
Table of contents
Enabling modules
Setting the HTML script tag
To start using modules, you need to add the type
attribute to your script tag and set its value to module
:
<!--- Script tag for modules ---> <script type="module" src="parent.js"></script>
Now, when you run the linking HTML file in your browser, it interprets parent.js
as a module. This enables the use of import
/export
syntax.
Running modules
Note that even with this change to the script tag, modules will only run on a live server.
If you are using Visual Studio Code, you can easily start a live server locally without any complicated setup. Just install the Live Server extension and choose to open your HTML document via the live server (right-click > Open with Live Server).
This will start running the linked .js
file as a module.
Using modules: import and export
To use import
and export
requires a second .js
file. So in addition to parent.js
, create a child.js
file in the same directory.
Now, let’s export something from child.js
and import it into parent.js
.
Exporting
There are two options for exporting: in-line or at the bottom of your script (generally preferable).
Let’s look at some examples of both below, exporting from child.js
. We’ll then import what is exported into parent.js
.
Exporting in-line
To export, you can use the export
keyword in-line before the definition of a function or variable containing data.
To keep the example interesting, we’ll export a function and a result from it that you may use in a real project: a function that generates a unique ID each time it is called:
/* Using the export keyword in-line */ // exporting a function export function generateID() { const time = Date.now(); const randomNumber = Math.floor(Math.random() * 1000000001); const uniqueId = time + "_" + randomNumber; return uniqueId; } // exporting a variable export const myID = generateID();
Exporting at bottom of module
You can also export functions and data at the bottom of a module. This is generally a cleaner solution, especially if many items are being exported:
/* Exporting at bottom of a module */ function generateID() { const time = Date.now(); const randomNumber = Math.floor(Math.random() * 1000000001); const uniqueId = time + "_" + randomNumber; return uniqueId; } const myID = generateID(); export { generateID, myID }
Changing export name
With either solution, you will now be able to import both generateID
and myID
into another module by specifying that you want to import either one.
You can change the name under which an export item is exported as follows:
/* Changing the name of exports */ export { generateID as makeID, myID as exampleID }
Setting a default export
In some modules, you may want to export only one item, or you want to export one item by default, unless otherwise specified.
In this case, add the keyword default to the item you want to make the default export for that modules:
/* Setting a default export */ export default generateID export { myID }
Now, when importing from this module, generateID
will be imported by default.
Importing
To import something exported by another module, use the import
keyword at the top of a module.
Importing regular exports
For an item that is not exported by default, use the following syntax:
/* Importing non-default exports */ import { generateID, myID } from './child.js' console.log(generateID); console.log(myID);
If the module you are exporting from has a default export, you do not need to include curly braces.
Importing a default export
You can also specify any valid variable name for the default export and can then use it under that name in your module:
/* Importing a default export */ import IDGenerator from './child.js' console.log(IDGenerator);
Importing a default and regular export(s)
To import a default export and regular export from a module, use a comma-separated list:
/* Importing a default and non-default export */ import IDGenerator, { myID } from './child.js' console.log(IDGenerator); console.log(myID);
Changing import name
You can use a non-default export under a different name in your module as follows:
/* Changing the name of an import */ import IDGenerator, { myID as exampleID } from './child.js' console.log(IDGenerator); console.log(exampleID);
Importing third-party libraries
A really cool feature of modules is that you can easily use code that others have written and made available in your project.
A popular third-party library that provides a lot of useful utility functions is lodash (see functions here).
The following code assumes that you have installed lodash in your root project directory from the command line. With npm:
cd "C:\Users\Me\Documents\my-project-directory" npm i --save lodash
Now, let’s see how you import it.
Example: importing lodash library and functions
By default, lodash exports its entire library of functions. Because the library is a default export, you can name it anything you like when importing it:
/* Importing the full library */ import _ from 'lodash';
You can then use its functions like this:
_.random(0, 10) // Returns a random number between 0 and 10 _.sum([4, 2, 8, 6]); // 20
Alternatively, you can import only the function or functions you will need in your project. You then don’t need to use dot notation to access a function within the library. Instead, you access the functions directly by their import reference name:
/* Importing individual functions */ import { random, sum } from 'lodash'; random(0, 10); sum([4, 2, 8, 6]);
On the face of it, this is a more efficient solution. However, Alexander Chertkov ran some tests and found little difference performance difference between importing the whole library and individual functions from it.
Given that importing functions individually also requires more maintenance, it is therefore probably preferable in practice to import the entire library.
Summary
Using JavaScript modules syntax, you can call upon data and functionality from other modules. Importantly, modules allow you to load only the code you need for your app. This means that as well as better organization of your code base, modules can lead to performance benefits.
You can also import cool third-party libraries into your project when needed, like lodash.
Related links
- Wikipedia: Modular programming
- Alexander Chertkov on BlazeMeter: The Correct Way to Import Lodash Libraries – A Benchmark