We lacked the native modules in JavaScript for quite some time. However, the community came up with some creative solutions such as CommonJS which is used in Node.js and Asynchronous Module Definition or AMD which RequireJS is the most popular implementation of it.

Nevertheless, the ES6 modules native support is probably one of the best features that have been added to the language IMO. The implementation was close to both of the systems above, which meant satisfaction for both CommonJS and AMD users.

The browsers and JS engines have a default “module loader”, which is overridable, and ES6 modules came to Node.js 8.5.0 behind the --experimental-modules flag.

ES6 modules are created per file which means each file has one module, and each module lives in one file. We use the export statement to export functions, objects, or primitive values from the module so they can be used by other programs with the import statement. Therefore, the import statement is used to import functions, *objects8, or primitives which are defined in and exported by an external module.

// export syntax
export const someData = 'data';

// import syntax
import { someData } from 'path/to/module/file'

Notice that we exclude the .js extension from the file that contains the module.

Now, of course, we would have the export of the structures in one file, and the import in another. There are two types (named and default) and a few ways of the export. The previous is the named type of the export, while the default looks something like the following.

// myDataModule.js
const someData = 'data';
export default someData;

Every module that we have can have only one default export, but it can have multiple named exports. The import name, in this case, can have whatever you prefer it to be.

// app.js
import thatData from './myDataModule';

console.log(thatData); // data

Although it is defined as someData in myDataModule.js, we get to name it whatever we prefer, which is in this case thatData. The following shows the ways of exporting in modules.

export { name1, name2, …, nameN };
export { variable1 as name1, variable2 as name2, …, nameN };
export let name1, name2, …, nameN; // also var, function
export let name1 = …, name2 = …, …, nameN; // also var, const

export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };

export * from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;

Just like export has these means of exporting module, import also has the following forms to import modules.

import defaultMember from "module-name";
import * as name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import defaultMember, { member [ , [...] ] } from "module-name";
import defaultMember, * as name from "module-name";
import "module-name";

One noticeable thing here is that we can import the entire module (that is all the exported parts) using import * syntax.