DEV Community

Cover image for JavaScript Modules: An Introduction
Nick Baughan
Nick Baughan

Posted on • Edited on

2 2

JavaScript Modules: An Introduction

When I began as a web developer, one of the first projects I worked on had multiple JavaScript files all loaded into the browser using script tags. This was a fragile method for managing multiple files. With one file having a dependency on another, the ordering of these script tags were critical. One script tag out of order would crash the application.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Script Tag Example</title>
</head>
<body>
    <script src="https://code.jquery.com/jquery-3.7.1.js"></script>
    <script src="./scripts/main.js"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

In the example above, since jquery.js is a dependency of main.js, failure to load JQuery will throw an error! Or if the ordering of the script tags were reversed, main.js would try and use JQuery before it had been loaded into the browser. In a simple example like this it doesn't seem too difficult to manage, but with a large production application with multiple developers, it was a nightmare. When trying to refactor and clean up old script tags, a mistake could crash the app.

Another problem was that everything was loaded onto the global scope. It wasn't very hard to overwrite a variable by accident. Declaring a new variable with the same name of an already existing variable and the first variable was replaced(This was before ES6 introduced "const"). This would lead to bugs and unexpected behavior.

JavaScript started as a simple scripting language for the browser, mostly used for form validation and adding a bit of interactivity to otherwise static html pages.
In time developers began using the language to build larger and more dynamic applications. The codebase being used for web applications was growing. Splitting code between files and using third party libraries was a headache. The JavaScript ecosystem needed a module system.

The JavaScript community began to experiment and design their own module systems. CommonJS and Asynchronous Module Definition were two early specifications. For a time I was most familiar with Asynchronous Module Definition, or AMD for short. Working with ESRI's JavaScript API, which was built on the Dojo Toolkit gave me plenty of experience with the AMD pattern.

But in 2015 the JavaScript specifications finally included a built in module system. JavaScript Modules or ES6 Modules, while not immediately implemented, is today the standard in all browsers and NodeJS.

JavaScript Modules uses import and export statements to share code between files.

There are multiple ways to export code.

Named Exports

Named Export Example

This is called a named export and will export the add function.

Using the below syntax, the function can be imported and used within another file.

Importing Named Export Example

You also have the ability to export multiple named exports, like in the snippet below

Exporting multiple Named Exports Example

correspondingly, you would import these like this.

Importing Multiple Named Exports Example

Another useful trick is to rename a named export when importing it.

Renaming Import Example

This will allow two named exports with the same name, to be used in the same file and avoid variable collision. This is most common when using multiple third party code, where you don't have control over naming.

Default Exports

Another way to export is using a default export.

Default export example

As a sidenote, a default export can be named within the exporting file and it will work the same. The name you give it will have no effect when importing it. The below snippet would work just the same.

const pi = 3.14;
export default pi;
Enter fullscreen mode Exit fullscreen mode

Naming the default export pie will work the same as pi. It really doesn't matter how you alias a default import, as long as it isn't a JavaScript reserved word.

import pie from "./constants.js";

const radius = 3;
const radiusSquared = radius * radius;

console.log(pie * radiusSquared);
Enter fullscreen mode Exit fullscreen mode

A file can have only one default export by definition and when importing it, it can be aliased with any name.

Another way that you will often see default exports used is with Object Property Shorthand. In the default export statement, an object literal will be created with all of the variables as the objects properties. The imported variable will be an object that contains all the exported functionality as the object's properties.

Default Export Object Property Shorthand Example

And of course you can mix named and default exports.

Mixed ExportsExample

Module Object

One last module pattern I will mention is creating a module object with the import. By using the syntax import * as [object variable] from [source] you can wrap all of the imported functionality into a convenient object in the consuming code. '[object variable]' can be replaced with a variable name the contain the imported object. '[source]' can be replaced with the file location or the module identifier to indicate where the code lives.

Take a look at the refactor below for an example. Now the consuming code is importing the functionality into an object being aliased as 'calculator', and has the methods 'add' and 'subtract' bundled into it. A nice way to keep your code a bit cleaner.

Module Objects

In the next article I will go into more details about module identifiers.

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

AWS Q Developer image

Your AI Code Assistant

Automate your code reviews. Catch bugs before your coworkers. Fix security issues in your code. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE