Welcome back to Jurassic Park! After building solid JavaScript foundations and learning asynchronous techniques, it's time to explore how to organize code into larger, scalable projects. In a world as complex as Jurassic Park, we need a well-organized structure to ensure system safety and efficiency.
Imagine managing Jurassic Park without dividing it into teams and departments — everything would be chaotic and dangerous! The same is true for code — when everything lives in one file, it quickly becomes unreadable and hard to maintain.
ES Modules (ECMAScript Modules) is the standard JavaScript module system that allows you to:
Let's start by creating a few basic modules for our Jurassic Park management system:
When we have one primary element to export, we use a default export:
1// dinosaur.js - Dinosaur model
2class Dinosaur {
3 constructor(name, species, diet, dangerLevel) {
4 this.name = name;
5 this.species = species;
6 this.diet = diet;
7 this.dangerLevel = dangerLevel;
8 this.health = 100;
9 this.isContained = true;
10 }
11
12 feed() {
13 this.health = Math.min(100, this.health + 20);
14 return `${this.name} has been fed. Health level: ${this.health}%`;
15 }
16
17 checkStatus() {
18 return {
19 name: this.name,
20 health: this.health,
21 isContained: this.isContained,
22 warning: this.dangerLevel > 7 ? "HIGH DANGER LEVEL" : ""
23 };
24 }
25}
26
27// Default export — only one class/function/object per file
28export default Dinosaur;When we have multiple elements to export, we use named exports:
1// securityUtils.js - Security utility functions
2export const SECURITY_LEVELS = {
3 GREEN: "normal",
4 YELLOW: "elevated",
5 ORANGE: "high",
6 RED: "critical"
7};
8
9export function checkFenceStatus(sectorId) {
10 // Simulate checking fence status in a given sector
11 const status = Math.random() > 0.1 ? "active" : "failure";
12 return {
13 sector: sectorId,
14 status,
15 timestamp: new Date().toISOString()
16 };
17}
18
19export function triggerAlarm(sector, reason) {
20 console.log(`ALARM! Sector ${sector}: ${reason}`);
21 // Alarm activation logic
22 return {
23 success: true,
24 alarmId: "ALR-" + Math.floor(Math.random() * 10000)
25 };
26}
27
28// We can also export a default function alongside named exports
29export default function getSecurityStatus() {
30 // Returns overall park security status
31 return {
32 level: SECURITY_LEVELS.GREEN,
33 activeAlarms: 0,
34 lastChecked: new Date().toISOString()
35 };
36}We can also define our variables, functions, or classes first and export them at the end of the file:
1// parkStats.js - Jurassic Park statistics
2const totalVisitors = 0;
3const revenue = 0;
4const incidents = [];
5
6function addVisitors(count) {
7 totalVisitors += count;
8 return totalVisitors;
9}
10
11function addRevenue(amount) {
12 revenue += amount;
13 return revenue;
14}
15
16function recordIncident(type, location, description) {
17 const incident = {
18 id: incidents.length + 1,
19 type,
20 location,
21 description,
22 timestamp: new Date().toISOString()
23 };
24 incidents.push(incident);
25 return incident;
26}
27
28function getDailyStats() {
29 return {
30 totalVisitors,
31 revenue,
32 incidentCount: incidents.length
33 };
34}
35
36// Export multiple elements at once
37export {
38 addVisitors,
39 addRevenue,
40 recordIncident,
41 getDailyStats
42};
43
44// We can also rename an exported element
45export { recordIncident as logParkIncident };After creating modules, it's time to use them in the main application:
1// app.js
2import Dinosaur from './dinosaur.js';
3
4// Create new dinosaur instances
5const trex = new Dinosaur("Rex", "Tyrannosaurus", "carnivore", 9);
6const brachi = new Dinosaur("Longneck", "Brachiosaurus", "herbivore", 4);
7
8console.log(trex.checkStatus());
9console.log(brachi.feed());1// securityMonitor.js
2import { checkFenceStatus, triggerAlarm, SECURITY_LEVELS } from './securityUtils.js';
3
4// Use imported functions and constants
5function monitorSecurity() {
6 const sectors = ["A1", "B2", "C3", "D4"];
7
8 // Check each sector
9 sectors.forEach(sector => {
10 const fenceStatus = checkFenceStatus(sector);
11
12 if (fenceStatus.status === "failure") {
13 const alarmResult = triggerAlarm(
14 sector,
15 "Electric fence failure!"
16 );
17 console.log(`Alarm ${alarmResult.alarmId} triggered in sector ${sector}`);
18
19 document.querySelector("#securityStatus").textContent = SECURITY_LEVELS.RED;
20 }
21 });
22}
23
24setInterval(monitorSecurity, 60000); // Check every minute1// dashboard.js
2import getSecurityStatus, { SECURITY_LEVELS, checkFenceStatus } from './securityUtils.js';
3
4// Use the default import
5const parkStatus = getSecurityStatus();
6console.log("Security status:", parkStatus.level);
7
8// Use the named imports
9if (parkStatus.level === SECURITY_LEVELS.RED) {
10 // Check all sectors immediately
11 ["A1", "B2", "C3", "D4"].forEach(sector => {
12 console.log(`Checking sector ${sector}: `, checkFenceStatus(sector));
13 });
14}1// incidentReport.js
2import { recordIncident as logIncident } from './parkStats.js';
3
4// Use the imported function under its new name
5function reportEmergency(location, details) {
6 logIncident("EMERGENCY", location, details);
7 // Additional emergency handling logic
8}1// adminPanel.js
2import * as ParkStats from './parkStats.js';
3
4// Access all module functions through the object
5function generateDailyReport() {
6 const visitorCount = ParkStats.addVisitors(120); // Adds 120 visitors
7 const dailyRevenue = ParkStats.addRevenue(15000); // Adds $15,000 revenue
8
9 const stats = ParkStats.getDailyStats();
10
11 return `
12 Jurassic Park Daily Report:
13 - Visitor count: ${stats.totalVisitors}
14 - Revenue: $${stats.revenue}
15 - Incidents: ${stats.incidentCount}
16 `;
17}In large projects, it's often useful to create entry points that aggregate and re-export functionality from multiple smaller modules:
1// models/index.js - Aggregate all models
2export { default as Dinosaur } from './dinosaur.js';
3export { default as Employee } from './employee.js';
4export { default as Facility } from './facility.js';
5export { default as Visitor } from './visitor.js';This lets us import all models from a single entry point:
1// main.js
2import { Dinosaur, Employee, Facility } from './models/index.js';
3
4// Now we can use all models
5const rex = new Dinosaur("Rex", "Tyrannosaurus", "carnivore", 9);
6const lab = new Facility("InGen Labs", "B3", 12);
7const scientist = new Employee("Dr. Wu", "Chief Geneticist", "lab");ES Modules also offer a dynamic import mechanism, allowing modules to be loaded on demand:
1// emergencySystem.js
2async function handleEmergency(type) {
3 let emergencyModule;
4
5 switch(type) {
6 case "dinosaurEscape":
7 emergencyModule = await import('./emergencies/dinosaurEscape.js');
8 break;
9 case "powerOutage":
10 emergencyModule = await import('./emergencies/powerOutage.js');
11 break;
12 case "weatherAlert":
13 emergencyModule = await import('./emergencies/weatherAlert.js');
14 break;
15 default:
16 emergencyModule = await import('./emergencies/generalEmergency.js');
17 }
18
19 // Execute emergency procedure from the loaded module
20 emergencyModule.initiateProtocol();
21}
22
23// UI button to trigger the emergency procedure
24document.querySelector("#emergencyButton").addEventListener("click", () => {
25 const emergencyType = document.querySelector("#emergencyType").value;
26 handleEmergency(emergencyType);
27});To use ES Modules directly in the browser, we must add the
type="module" attribute to the script tag:1<!DOCTYPE html>
2<html>
3<head>
4 <title>Jurassic Park Control Panel</title>
5</head>
6<body>
7 <h1>Jurassic Park Control Panel</h1>
8 <div id="securityStatus"></div>
9 <select id="emergencyType">
10 <option value="dinosaurEscape">Dinosaur escape</option>
11 <option value="powerOutage">Power outage</option>
12 <option value="weatherAlert">Weather alert</option>
13 </select>
14 <button id="emergencyButton">Activate emergency procedure</button>
15
16 <!-- Main script using modules -->
17 <script type="module" src="./app.js"></script>
18</body>
19</html>Organizing applications with ES Modules offers many benefits:
Let's see how we might organize a larger dinosaur monitoring system for Jurassic Park using modules:
1// models/dinosaur.js
2export default class Dinosaur {
3 // ... Dinosaur class implementation (as above)
4}
5
6// services/dinoTracking.js
7import Dinosaur from '../models/dinosaur.js';
8
9export async function getAllDinosaurs() {
10 // Fetch dinosaur data from API
11 const response = await fetch('/api/dinosaurs');
12 const data = await response.json();
13
14 // Convert raw data to Dinosaur class instances
15 return data.map(dino => new Dinosaur(
16 dino.name,
17 dino.species,
18 dino.diet,
19 dino.dangerLevel
20 ));
21}
22
23export async function getDinosaursByDangerLevel(level) {
24 const allDinos = await getAllDinosaurs();
25 return allDinos.filter(dino => dino.dangerLevel >= level);
26}
27
28// services/securitySystem.js
29export async function getActiveFences() {
30 // Fetch active fence data
31 const response = await fetch('/api/fences/active');
32 return response.json();
33}
34
35export async function activateSectorLockdown(sectorId) {
36 // Activate lockdown in a given sector
37 const response = await fetch(`/api/sectors/${sectorId}/lockdown`, {
38 method: 'POST'
39 });
40 return response.json();
41}
42
43// utils/alertSystem.js
44export const ALERT_LEVELS = {
45 INFO: 'info',
46 WARNING: 'warning',
47 DANGER: 'danger',
48 CRITICAL: 'critical'
49};
50
51export function showAlert(message, level = ALERT_LEVELS.INFO) {
52 const alertElement = document.createElement('div');
53 alertElement.className = `alert alert-${level}`;
54 alertElement.textContent = message;
55
56 document.querySelector('#alertContainer').appendChild(alertElement);
57
58 // Remove alert after 5 seconds (unless it's critical)
59 if (level !== ALERT_LEVELS.CRITICAL) {
60 setTimeout(() => {
61 alertElement.remove();
62 }, 5000);
63 }
64}
65
66// controllers/monitoringDashboard.js
67import { getAllDinosaurs, getDinosaursByDangerLevel } from '../services/dinoTracking.js';
68import { getActiveFences, activateSectorLockdown } from '../services/securitySystem.js';
69import { showAlert, ALERT_LEVELS } from '../utils/alertSystem.js';
70
71export async function initializeDashboard() {
72 try {
73 // Fetch dinosaur data and fence status
74 const [allDinos, activeFences] = await Promise.all([
75 getAllDinosaurs(),
76 getActiveFences()
77 ]);
78
79 // Find dangerous dinosaurs
80 const dangerousDinos = await getDinosaursByDangerLevel(8);
81
82 // Display data on the dashboard
83 renderDinosaurTable(allDinos);
84 renderSecurityStatus(activeFences);
85
86 // Show alerts for dangerous dinosaurs
87 if (dangerousDinos.length > 0) {
88 showAlert(
89 `WARNING: ${dangerousDinos.length} dinosaurs with high danger level`,
90 ALERT_LEVELS.WARNING
91 );
92 }
93
94 // Set up emergency event listeners
95 setupEmergencyHandlers();
96
97 } catch (error) {
98 showAlert(`Dashboard initialization error: ${error.message}`, ALERT_LEVELS.DANGER);
99 }
100}
101
102function renderDinosaurTable(dinosaurs) {
103 // Dinosaur table rendering logic
104}
105
106function renderSecurityStatus(fences) {
107 // Security status rendering logic
108}
109
110function setupEmergencyHandlers() {
111 // Emergency event handler setup logic
112 document.querySelector('#emergencyLockdown').addEventListener('click', async () => {
113 const sector = document.querySelector('#sectorSelect').value;
114 try {
115 await activateSectorLockdown(sector);
116 showAlert(`Sector ${sector} lockdown activated`, ALERT_LEVELS.WARNING);
117 } catch (error) {
118 showAlert(`Failed to activate lockdown: ${error.message}`, ALERT_LEVELS.CRITICAL);
119 }
120 });
121}
122
123// app.js - Main application file
124import { initializeDashboard } from './controllers/monitoringDashboard.js';
125
126// Initialize application when DOM is ready
127document.addEventListener('DOMContentLoaded', () => {
128 initializeDashboard();
129
130 // Additional initialization logic
131 console.log('Jurassic Park monitoring system started');
132});Modern browsers support ES Modules natively, but for full compatibility with older browsers, we often use tools like Webpack or Rollup to bundle our modules into code compatible with older browsers.
1// webpack.config.js
2const path = require('path');
3
4module.exports = {
5 entry: './src/app.js',
6 output: {
7 filename: 'bundle.js',
8 path: path.resolve(__dirname, 'dist')
9 },
10 module: {
11 rules: [
12 {
13 test: /.js$/,
14 exclude: /node_modules/,
15 use: {
16 loader: 'babel-loader',
17 options: {
18 presets: ['@babel/preset-env']
19 }
20 }
21 }
22 ]
23 }
24};ES Modules is a powerful JavaScript code organization mechanism that allows you to create modular, well-organized applications. In a world as complex as Jurassic Park, where every mistake can have catastrophic consequences, good code organization is critical to maintaining safety and efficiency.
In the next exercise we'll look at an alternative module system — CommonJS, which is widely used in the Node.js environment, ideal for building the server-side part of our Jurassic Park management system.