ES6 modules are a way to organize and structure JavaScript code by dividing it into reusable pieces. Introduced in ES6 (ECMAScript 2015), modules allow developers to define code in separate files and explicitly export or import only the parts needed. This improves maintainability, reusability, and scalability in projects.

Key Concepts of ES6 Modules

  • Exporting: Modules can export variables, functions, or classes so that other modules can use them.
    Named Exports: Export multiple items from a module using unique names.
    Default Exports: Export a single default item from a module.
  • Importing: Other modules can import the exported items using their names (for named exports) or a default import identifier (for default exports).
  • Scope: ES6 modules have their own scope. Variables and functions declared in a module are not added to the global scope.

Named Exports and Imports

A module can export multiple values by name.

math.js (Module)

export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const multiply = (a, b) => a * b;

main.js (Importing Module)

import { add, subtract } from './math.js';

console.log(add(2, 3)); // 5
console.log(subtract(5, 2)); // 3

You can also rename imports:

import { add as addition } from './math.js';

console.log(addition(3, 4)); // 7

Default Exports and Imports

A module can export one default value. This is often used when the module is exporting a single functionality or object.

utils.js (Module)

export default function greet(name) {
    return `Hello, ${name}!`;
}

main.js (Importing Module)

import greet from './utils.js';

console.log(greet('Alice')); // Hello, Alice!

Default imports can have any name:

import welcome from './utils.js';

console.log(welcome('Bob')); // Hello, Bob!

Combining Named and Default Exports

You can mix named and default exports in the same file.

shapes.js (Module)

export const PI = 3.14159;

export function areaOfCircle(radius) {
    return PI * radius * radius;
}

export default class Circle {
    constructor(radius) {
        this.radius = radius;
    }

    getArea() {
        return areaOfCircle(this.radius);
    }
}

main.js (Importing Module)

import Circle, { PI, areaOfCircle } from './shapes.js';

const myCircle = new Circle(5);

console.log(myCircle.getArea()); // 78.53975
console.log(PI); // 3.14159
console.log(areaOfCircle(3)); // 28.274309999999996

Re-exporting Modules

Modules can re-export items from another module, which is useful for creating a central "index" file.

math.js

export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;

index.js

export { add, subtract } from './math.js';

main.js

import { add } from './index.js';

console.log(add(4, 7)); // 11

Dynamic Imports

Dynamic imports allow you to load modules dynamically at runtime using import(). This is useful for code-splitting and lazy loading.

logger.js

export default function logMessage(message) {
    console.log(`Log: ${message}`);
}

main.js

if (someCondition) {
    import('./logger.js').then(({ default: logMessage }) => {
        logMessage('Dynamic import works!');
    });
}

ES6 Module Syntax in Browsers

To use ES6 modules in a browser, ensure your script has the type="module" attribute:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ES6 Modules</title>
</head>
<body>
    <script type="module" src="main.js"></script>
</body>
</html>

ES6 modules provide a clean, standardized way to organize and share code in JavaScript, enabling better collaboration and modularity in projects.