Course: Section

Get Started with Modern React: Intro, Setup, and ES6 Basics

Episode: Title

S01・V13: Modules

Date Created: July 17th, 2019
Last Updated: 27 days ago

Objectives
  1. We will explain why JavaScript modules, also called “ES modules”, are necessary.
  2. We will show you an example of applying a module to an HTML page.
  3. We will explain the difference between named exports and default exports.
  4. We will show you how to use a module object to effectively create a namespace when importing code.
Watch Video
Duration: 7m 38s

ES Modules

In the early days, most of the usage of JavaScript was to do isolated scripting tasks, providing a bit of interactivity to your web pages where needed, so large scripts were generally not necessary. Fast forward a few years, and we now have complete applications being run in browsers, with a lot of JavaScript.

It has therefore made sense in recent years to start thinking about providing mechanisms for splitting JavaScript programs up into separate modules, that can be imported when required. Most modern browsers have started to support module functionality natively, so that browser clients can optimize the loading of modules, instead of us having to use a library, and perform extra client-side processing and round trips to the server.

To demonstrate how to use modules, we have applied an ES module to an HTML page.

Within our base modules/ folder, we have an index.html file and an index.js file, and a subfolder called utils/, which has a file called money.js inside.

File structure

Let’s start with the index.html page, which is a bare bones HTML page, with some styles added.

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>ES6</title>
    <link href="#" rel="shortcut icon" />
    <style>
      body {
        background-color: #222;
      }
      h1 {
        color: white;
        font-family: sans-serif;
        margin: 0;
      }
    </style>
  </head>
  <body>
    <h1>Modules</h1>
    <script src="index.js" type="module"></script>  </body>
</html>

We have added a script tag to the bottom of the body of the page. Note that the script tag has a type attribute, with the value module.

To test our modules locally on our development machine, we need to run a local web server. For example, you can run a local web server from the base folder of your project in Terminal.

Use the command npx serve to start a web server, and then navigate to localhost:5000 in your browser to test the modules.

Exporting

We will now move to the money.js file.

In this file, we will add a utility function for calculating the compound interest on a principal amount of dollars invested. Next, we will add a utility function that takes a number representing a dollar amount, and converts it into a nicely formatted string. Don’t worry about the details of these functions. We will just use them to illustrate how to export modules.

utils/money.js
function compoundInterest (principal, interestRate, years) {
  return principal * (Math.E ** (interestRate * years));
}

function formatAmount (amount) {
  return `$${amount.toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')}`;
}

There are several ways to export the functions in this file. One way is to put the function names within curly braces { compoundInterest, formatAmount } at the bottom of the file, preceded by the export keyword.

utils/money.js
function compoundInterest (principal, interestRate, years) {
  return principal * (Math.E ** interestRate * years);
}

function formatAmount (amount) {
  return `$${amount.toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')}`;
}

export { compoundInterest, formatAmount };

Importing

Now, let’s go to the index.js file.

We can import the functions from the money.js file by using the import keyword, followed by the function names within curly braces { compoundInterest, formatAmount }. This is followed by the keyword from, and then the path to the file from which we are importing.

index.js
import { compoundInterest, formatAmount } from "./utils/money.js";

Let’s look at the path ./utils/money.js. The . at the start of the path string indicates that we are specifying a relative path starting from the current folder. We then move down into the utils/ folder, in which the money.js file can be found.

Now that we have imported the utility functions, we can use them within the index.js file. For example, let’s find out the amount amt that $10,000 grows to when invested at 5% interest, for 30 years.

index.js
import { compoundInterest, formatAmount } from "./utils/money.js";

const amt = compoundInterest(10000, 0.05, 30);

Now, let’s format the amount amt, and assign the result to a variable named fmt.

index.js
import { compoundInterest, formatAmount } from "./utils/money.js";

const amt = compoundInterest(10000, 0.05, 30);
const fmt = formatAmount(amt);

Now, print the results to the Console.

index.js
import { compoundInterest, formatAmount } from "./utils/money.js";

const amt = compoundInterest(10000, 0.05, 30);
const fmt = formatAmount(amt);

console.log("amt:", amt); // Prints `amt: 44816.890703380646`
console.log("fmt:", fmt); // Prints `fmt: "$44,816.89"`

You can see in the Console that the investment grows to over $44,000.

Named Exports vs Default Exports

The exports we currently have in utils/money.js are called “named exports”.

We can also specify a “default export”, using the export default keywords. We will make the formatAmount() function a default export, instead of a named export.

utils/money.js
function compoundInterest (principal, interestRate, years) {
  return principal * (Math.E ** (interestRate * years));
}

function formatAmount (amount) {
  return `$${amount.toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')}`;
}

export { compoundInterest };export default formatAmount;

Note that only one default export is allowed per module.

Within index.js, we need to import code, which has been exported with a default export, in a different way. We specify the import without the curly brackets when it comes to code exported as a default export, and we put it before any named variables:

index.js
import formatAmount, { compoundInterest } from "./utils/money.js";
const amt = compoundInterest(10000, 0.05, 30);
const fmt = formatAmount(amt);

console.log("amt:", amt); // Prints `amt: 44816.890703380646`
console.log("fmt:", fmt); // Prints `fmt: "$44,816.89"`

Effectively Namespace with a Module Object

Sometimes importing modules can lead to naming conflicts. One good way to avoid naming conflicts is to import code inside a “module object”.

To demonstrate this, we will revert back to using only named exports in utils/money.js.

utils/money.js
function compoundInterest (principal, interestRate, years) {
  return principal * (Math.E ** interestRate * years);
}

function formatAmount (amount) {
  return `$${amount.toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')}`;
}

export { compoundInterest, formatAmount };

Going back to index.js, we can import code inside a module object with the following syntax:

index.js
// import { compoundInterest, formatAmount } from "./utils/money.js";
import * as Money from "./utils/money.js";
// const amt = compoundInterest(10000, 0.05, 30);
// const fmt = formatAmount(amt);
const amt = Money.compoundInterest(10000, 0.05, 30);const fmt = Money.formatAmount(amt);
console.log("amt:", amt); // Prints `amt: 44816.890703380646`
console.log("fmt:", fmt); // Prints `fmt: "$44,816.89"`

This grabs all the named exports from utils/money.js, as indicated by the asterisk * above after the import keyword, and makes them available as (viz. as) members of an object called Money, effectively giving it its own namespace.

Summary

We explained why JavaScript modules, also called “ES modules”, are necessary. We showed you an example of applying a module to an HTML page. We explained the difference between named exports and default exports. And, we showed you how to use a module object to effectively create a namespace when importing code.

Next Up…

In the next video, we will test what we have learned in Section 1 using some “Challenges”.