Categories
JavaScript JavaScript Basics

Introduction to JavaScript Modules

Spread the love

JavaScript modules allow us to divide code into small pieces. They also let us keep some code private while exposing other pieces of code that can be imported into another module.

In this article, we’ll look at how to use define and use them.

Named Exports

Names exports start with the export keyword and they expose items from a module to the outside. Then we can import them somewhere else.

There can be multiple named exports in one module. For instance, we can write:

export const foo = 1;  
export let bar = 1;

Then we can import them into another module as follows:

import { foo, bar } from "./module";
const baz = foo + bar;

We can also import the whole module with the * sign as follows:

import * as module from "./module";
const baz = module.foo + module.bar;

Default Export

We can export one default export using the export default keywords. For instance, we can write:

export default 1;

Then import it as follows:

import foo from "./bar";  
const baz = foo;

We can also export functions and class as follows:

export default () => {};

or:

export default class {}

We don’t need a semicolon at the end of the class export.

Also, we can define default exports with the following code:

const foo = 1;  
export { foo as default };

Browser Differences Between Scripts and Modules

In browsers, scripts are denoted by the script tag. Modules are the same, but it’s denoted by the type attribute with value module .

Scripts are not in strict mode by default, while modules are in strict mode by default.

Top-level variables are global in scrips and are local to the module in modules.

Top-level value of this is window in scripts and it’s undefined in modules.

Scripts are run synchronously while modules are run asynchronously.

There are no import statements in scripts and we can selectively import module members in modules.

We can programmatically import both scripts and modules using promise-based APIs.

Module Characteristics

ES6 modules can be statically analyzed for static checking, optimization, and more. It has a declarative syntax for importing and exporting.

Imports are hoisted to the top so that they can be referenced anywhere in the module.

For instance, if we have:

export const foo = 1;

Then we can import it as follows:

const baz = foo;
import { foo } from "./bar";

Also, they must be at the top-level. Therefore, we can’t have something like:

{  
  import { foo } from "./bar";  
  const baz = foo;  
}

Imports are Read-Only Views on Exports

ES6 imports are read-only views on export entities. Connections to variables inside the module that imported the export remain live.

For instance, if we have:

export const foo = 1;

Then if we have the following:

import { foo } from "./bar";  
foo = 1;

Then we’ll get a ‘”foo” is read-only.’ error since it’s referencing the export directly in a read-only manner.

We get the same result if we change the const to let .

Cyclic Dependencies

If module A and B import members from each other, then we call it a cyclic dependency. This is supported with ES6 modules. For instance, if we have:

index.js :

import { foo } from "./bar";
export const baz = 2;

bar.js :

import { baz } from "./index";
export let foo = 1;

Then the modules are cyclic dependencies since we import a member from bar inindex and we import a member from index in bar .

This works because imports just refer to the original data, so it doesn’t matter when they come from.

Importing Styles

We can import JavaScript modules in various ways. One way is the default import, which is how we import members from a module.

For instance, we can write:

bar.js :

let foo = 1;  
export default foo;

Then we can import it as follows:

import foo from "./bar";

Named imports can be imported as follows. Given the following named exports:

export let foo = 1;

Then we can import it as follows:

import { foo } from "./bar";

We can rename named exports by using the as keyword as follows:

import { foo as baz } from "./bar";

We can also rename default exports as follows:

import { default as foo } from "./bar";

We can also have empty where we don’t import anything. Instead, we run what’s included in the module.

For instance, if we have the following in bar.js :

console.log("bar");

Then we can run the code in bar.js as follows:

import "./bar";

Therefore, we should see 'bar' logged in the console log.

Conclusion

ES6 modules are a great way to divide code into small chunks. We can export module members and import them in another file. Imported members are read-only. Modules are in strict mode by default, so we avoid lots of issues with non-strict mode.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *