- ES Modules are a standardized module system introduced in ECMAScript 2015 (ES6).
- They allow you to export functions, objects, or primitive values from a module so they can be used by other programs with the import statement.
- Each module has its own scope, which means variables and functions declared in one module are not accessible in another unless explicitly exported and imported.
- Isolation of Scope: Prevents global namespace pollution.
- Dependency Management: Clearly defines dependencies between modules.
- Improved Maintainability: Easier to manage and update code.
- Enhanced Reusability: Modules can be reused across different parts of an scriptlication or even in different projects.
By encapsulating code within modules, you prevent variables and functions from unintentionally interfering with each other. This is especially important in large scriptlications where multiple developers might define variables with the same name.
Modules help break down a complex scriptlication into manageable pieces. Each module handles a specific functionality, making the codebase easier to understand and navigate.
Modules can be reused in different parts of the scriptlication or even in other projects. This promotes code reuse and reduces duplication.
With modules, you can update or fix code in one place without affecting other parts of the scriptlication. This makes it easier to manage large codebases.
Multiple developers can work on different modules simultaneously without stepping on each other's toes, improving teamwork and productivity.
- Exporting is how a module makes certain functions, objects, or values available to other modules.
- There are two types of exports:
- Named Exports: Export multiple bindings (variables, functions) by name.
- Default Exports: Export a single value or to have a fallback value for your module.
- Importing is how you include exported bindings from other modules into your module.
- You can import named exports, default exports, or all exports at once.
Let's dive deep into an example module that deals with day names.
// dayname.js
// A private constant not exported
const names = ["Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"];
// Exported function to get day name from number
export function dayName(number) {
return names[number];
}
// Exported function to get day number from name
export function dayNumber(name) {
return names.indexOf(name);
}
-
Private Constant
names
const names = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
- This array contains the names of the days of the week.
- It is not exported, so it's private to the module.
- Other modules cannot access
names
directly.
-
Exported Function
dayName
export function dayName(number) { return names[number]; }
- The
export
keyword makes this function available to other modules. - Purpose: Given a day number (0-6), it returns the corresponding day name.
- Usage: If you pass
3
, it returns"Wednesday"
.
- The
-
Exported Function
dayNumber
export function dayNumber(name) { return names.indexOf(name); }
- Also exported for use in other modules.
- Purpose: Given a day name, it returns the corresponding day number.
- Usage: If you pass
"Friday"
, it returns5
.
// script.js
// Importing the dayName function from dayname.js
import { dayName } from "./dayname.js";
let now = new Date();
console.log(`Today is ${dayName(now.getDay())}`);
// Outputs: Today is Monday (or whatever the current day is)
-
Import Statement
import { dayName } from "./dayname.js";
- Syntax:
import { exportedMember } from "modulePath";
- We're importing the
dayName
function fromdayname.js
. - The path
"./dayname.js"
tells the module loader where to find the module.
- Syntax:
-
Using the Imported Function
let now = new Date(); console.log(`Today is ${dayName(now.getDay())}`);
now.getDay()
returns the day of the week as a number (0-6).dayName(now.getDay())
converts that number into the corresponding day name.- The result is logged to the console, e.g., "Today is Monday".
You can export variables, functions, or classes from a module so they can be used in other modules.
// math.js
// Exporting a function
export function add(a, b) {
return a + b;
}
// Exporting a variable
export const PI = 3.1416;
- Exported Members:
add
,PI
. - Other modules can import these exported members.
You can import exported members from other modules into your current module.
// script.js
import { add, PI } from "./math.js";
console.log(add(2, 3)); // Outputs: 5
console.log(PI); // Outputs: 3.1416
- Syntax:
import { member1, member2 } from "modulePath";
- Imports the
add
function andPI
constant frommath.js
.
A module can have one default export, which is the main exported value.
// greeting.js
export default function greet(name) {
return `Hey, ${name}!`;
}
- The
export default
keyword is used. - The default export can be a function, class, object, or any value.
// script.js
import greet from "./greeting.js";
console.log(greet("Muhammad Hashim")); // Outputs: Hey, Muhammad Hashim!
- Syntax:
import name from "modulePath";
- No braces are used when importing a default export.
- You can name the imported value whatever you like.
You can rename imported members using the as
keyword.
import { dayName as getDayName } from "./dayname.js";
console.log(getDayName(3)); // Outputs: Wednesday
dayName
is imported and renamed togetDayName
.- Useful to avoid naming conflicts or for clarity.
You can import all exported members of a module as properties of an object.
import * as day from "./dayname.js";
console.log(day.dayName(3)); // Outputs: Wednesday
console.log(day.dayNumber("Friday")); // Outputs: 5
- Syntax:
import * as name from "modulePath";
- All exports from
dayname.js
are available as properties of theday
object.
-
In Browsers: Module paths are treated as URLs.
import { something } from "/modules/someModule.js";
-
In Node.js: Module paths are resolved to file system paths.
import { something } from "./someModule.js";
-
Note: The way module paths are resolved can differ depending on the environment. Always ensure the paths are correct for your setup.
-
Top-Level Declarations:
import
andexport
statements must be at the top level of your module, not inside functions or blocks.// Valid export function myFunction() { /*...*/ } // Invalid if (condition) { export function anotherFunction() { /*...*/ } }
-
Static Resolution: Imports and exports are resolved at load time, before the code runs.
File: utils.js
// utils.js
// Exporting multiple utility functions
export function formatDate(date) {
// Formats a date object into YYYY-MM-DD string
const year = date.getFullYear();
const month = (`0${date.getMonth() + 1}`).slice(-2); // Months are zero-based
const day = (`0${date.getDate()}`).slice(-2);
return `${year}-${month}-${day}`;
}
export function capitalize(str) {
// Capitalizes the first letter of a string
if (!str) return '';
return str.charAt(0).toUpperCase() + str.slice(1);
}
export function generateRandomNumber(max) {
// Generates a random integer between 0 and max - 1
return Math.floor(Math.random() * max);
}
- Purpose: Provides common utility functions used across the scriptlication.
- Functions Exported:
formatDate
: Converts aDate
object into a string formatted asYYYY-MM-DD
.capitalize
: Capitalizes the first character of a given string.generateRandomNumber
: Returns a random integer between0
andmax - 1
.
Usage:
// script.js
import { formatDate, capitalize, generateRandomNumber } from "./utils.js";
const today = new Date();
console.log(`Today's date is ${formatDate(today)}`);
// Outputs: Today's date is 2023-10-15 (example date)
const name = "john";
console.log(`Hello, ${capitalize(name)}!`);
// Outputs: Hello, John!
console.log(`Random number: ${generateRandomNumber(100)}`);
// Outputs a random number between 0 and 99
-
Importing Functions
- We import
formatDate
,capitalize
, andgenerateRandomNumber
fromutils.js
. - This allows us to use these utility functions in
script.js
.
- We import
-
Using
formatDate
const today = new Date(); console.log(`Today's date is ${formatDate(today)}`);
- Creates a new
Date
object representing the current date. - Passes
today
toformatDate
, which returns a formatted string. - The formatted date is interpolated into a string and logged.
- Creates a new
-
Using
capitalize
const name = "hashim"; console.log(`Hello, ${capitalize(name)}!`);
- Defines a string
name
with the value"john"
. - Passes
name
tocapitalize
, which returns"John"
. - The capitalized name is included in a greeting message.
- Defines a string
-
Using
generateRandomNumber
console.log(`Random number: ${generateRandomNumber(100)}`);
- Calls
generateRandomNumber
with100
as the maximum value. - Generates a random integer between
0
and99
. - The random number is logged to the console.
- Calls
File: user.js
// user.js
export default class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
getProfile() {
return `${this.name} <${this.email}>`;
}
sendEmail(subject, message) {
// Simulate sending an email
console.log(`Sending email to ${this.email}...`);
console.log(`Subject: ${subject}`);
console.log(`Message: ${message}`);
}
}
- Purpose: Defines a
User
class representing a user in the scriptlication. - Class Exported:
- Exported as the default export, so it can be imported without braces.
- Class Methods:
constructor(name, email)
: Initializes a new user withname
andemail
.getProfile()
: Returns a formatted string with the user's name and email.sendEmail(subject, message)
: Simulates sending an email to the user.
Usage:
// script.js
import User from "./user.js";
const user = new User("Muhammad Hashim", "hashiimtahir@gmail.com");
console.log(user.getProfile());
user.sendEmail("Welcome!", "Thank you for joining our platform.");
// Simulates sending an email to the user
-
Importing the
User
Class- We import the default export from
user.js
, naming itUser
. - Since it's a default export, we don't use braces in the import statement.
- We import the default export from
-
Creating a New User Instance
const user = new User("Alice", "alice@example.com");
- Instantiates a new
User
object with the name"Alice"
and email"alice@example.com"
.
- Instantiates a new
-
Using
getProfile
Methodconsole.log(user.getProfile());
- Calls
getProfile()
on theuser
instance. - The method returns a string in the format
"Alice <alice@example.com>"
. - The result is logged to the console.
- Calls
-
Using
sendEmail
Methoduser.sendEmail("Welcome!", "Thank you for joining our platform.");
- Calls
sendEmail()
with a subject and message. - The method simulates sending an email by logging details to the console.
- Calls
-
Console Output
Alice <alice@example.com> Sending email to alice@example.com... Subject: Welcome! Message: Thank you for joining our platform.
- Shows the output from the
getProfile()
andsendEmail()
methods.
- Shows the output from the
File: constants.js
// constants.js
// Exporting mathematical constants
export const PI = 3.1416;
export const EULER = 2.7183;
export const GOLDEN_RATIO = 1.6180;
- Purpose: Provides common mathematical constants.
- Constants Exported:
PI
: The mathematical constant π.EULER
: The base of natural logarithms (e).GOLDEN_RATIO
: An irrational number often encountered in mathematics and art.
Usage:
// mathOperations.js
import { PI, EULER, GOLDEN_RATIO } from "./constants.js";
export function calculateCircumference(radius) {
// Calculates the circumference of a circle
return 2 * PI * radius;
}
export function exponentialGrowth(rate, time) {
// Calculates exponential growth
return EULER ** (rate * time);
}
export function goldenRectangle(width) {
// Calculates the height of a golden rectangle given its width
return width * GOLDEN_RATIO;
}
-
Importing Constants
- We import
PI
,EULER
, andGOLDEN_RATIO
fromconstants.js
. - These constants are used in mathematical calculations.
- We import
-
Defining
calculateCircumference
Functionexport function calculateCircumference(radius) { return 2 * PI * radius; }
- Calculates the circumference of a circle using the formula
C = 2πr
. - Uses the imported
PI
constant.
- Calculates the circumference of a circle using the formula
-
Defining
exponentialGrowth
Functionexport function exponentialGrowth(rate, time) { return EULER ** (rate * time); }
- Calculates exponential growth using the formula
e^(rt)
. - Uses the imported
EULER
constant.
- Calculates exponential growth using the formula
-
Defining
goldenRectangle
Functionexport function goldenRectangle(width) { return width * GOLDEN_RATIO; }
- Calculates the height of a golden rectangle given its width.
- Uses the imported
GOLDEN_RATIO
constant.
Using the Functions:
// script.js
import { calculateCircumference, exponentialGrowth, goldenRectangle } from "./mathOperations.js";
const radius = 5;
console.log(`Circumference of circle: ${calculateCircumference(radius)}`);
// Outputs: Circumference of circle: 31.416
const growth = exponentialGrowth(0.05, 10);
console.log(`Exponential growth after 10 units: ${growth}`);
// Outputs a calculated value based on the rate and time
const width = 10;
console.log(`Height of golden rectangle: ${goldenRectangle(width)}`);
// Outputs: Height of golden rectangle: 16.18
-
Importing Functions
- We import
calculateCircumference
,exponentialGrowth
, andgoldenRectangle
frommathOperations.js
.
- We import
-
Using
calculateCircumference
const radius = 5; console.log(`Circumference of circle: ${calculateCircumference(radius)}`);
- Sets
radius
to5
. - Calls
calculateCircumference(5)
which computes2 * PI * 5
. - Logs the result:
31.416
.
- Sets
-
Using
exponentialGrowth
const growth = exponentialGrowth(0.05, 10); console.log(`Exponential growth after 10 units: ${growth}`);
- Calls
exponentialGrowth
with a rate of0.05
and time10
. - Computes
EULER
raised to the power of(0.05 * 10)
. - Logs the calculated growth value.
- Calls
-
Using
goldenRectangle
const width = 10; console.log(`Height of golden rectangle: ${goldenRectangle(width)}`);
- Sets
width
to10
. - Calls
goldenRectangle(10)
which computes10 * GOLDEN_RATIO
. - Logs the result:
16.18
.
- Sets
File: products.js
// products.js
// Private array of products (not exported)
const products = [
{ id: 1, name: "Laptop", price: 999 },
{ id: 2, name: "Smartphone", price: 599 },
{ id: 3, name: "Tablet", price: 399 },
];
// Exported functions to interact with products
export function getProductById(id) {
return products.find(product => product.id === id);
}
export function getAllProducts() {
return products;
}
export function addProduct(newProduct) {
// Adds a new product to the array
products.push(newProduct);
}
- Purpose: Manages product data for an e-commerce scriptlication.
- Private Data:
products
array is private to the module and not exported.
- Functions Exported:
getProductById
: Retrieves a product by itsid
.getAllProducts
: Returns a copy of all products.addProduct
: Adds a new product to the collection.
Usage:
// script.js
import { getProductById, getAllProducts, addProduct } from "./products.js";
// Get a product by ID
const product = getProductById(2);
console.log(`Product: ${product.name}, Price: $${product.price}`);
// Outputs: Product: Smartphone, Price: $599
// Get all products
const allProducts = getAllProducts();
allProducts.forEach(p => {
console.log(`${p.id}: ${p.name} - $${p.price}`);
});
// Outputs a list of all products
// Add a new product
addProduct({ id: 4, name: "Headphones", price: 199 });
console.log("After adding a new product:");
// Get updated list of products
const updatedProducts = getAllProducts();
updatedProducts.forEach(p => {
console.log(`${p.id}: ${p.name} - $${p.price}`);
});
// Now includes the new product
-
Importing Functions
- We import
getProductById
,getAllProducts
, andaddProduct
fromproducts.js
.
- We import
-
Using
getProductById
const product = getProductById(2); console.log(`Product: ${product.name}, Price: $${product.price}`);
- Calls
getProductById(2)
to retrieve the product withid
2
. - Logs the product's
name
andprice
.
- Calls
-
Using
getAllProducts
const allProducts = getAllProducts(); allProducts.forEach(p => { console.log(`${p.id}: ${p.name} - $${p.price}`); });
- Calls
getAllProducts()
to retrieve all products. - Iterates over the array and logs each product's details.
- Calls
-
Using
addProduct
addProduct({ id: 4, name: "Headphones", price: 199 }); console.log("After adding a new product:");
- Calls
addProduct
with a new product object. - Adds the new product to the
products
array inproducts.js
.
- Calls
-
Verifying the Addition
const updatedProducts = getAllProducts(); updatedProducts.forEach(p => { console.log(`${p.id}: ${p.name} - $${p.price}`); });
- Calls
getAllProducts()
again to get the updated list. - The new product is now included in the array.
- Logs the updated list to verify the addition.
- Calls
-
Console Output
Product: Smartphone, Price: $599 1: Laptop - $999 2: Smartphone - $599 3: Tablet - $399 After adding a new product: 1: Laptop - $999 2: Smartphone - $599 3: Tablet - $399 4: Headphones - $199
- Shows the product details before and after adding the new product.