JavaScript and TypeScript course Β· Module 5: Advanced JavaScript
ES Modules (import/export)
In this lesson11
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.
Code Modularization in JavaScript
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:
- Split code into smaller, more manageable pieces
- Control which elements are accessible outside a module
- Define dependencies between different parts of a program
Exporting Elements from Modules
Let's start by creating a few basic modules for our Jurassic Park management system:
1. Default Export
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;2. Named Exports
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}3. Exporting Previously Defined Variables
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 };Importing Elements from Modules
After creating modules, it's time to use them in the main application:
1. Default Import
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());2. Named Import
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 minute3. Default and Named Import Together
1// 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}4. Import with Alias
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}5. Import Everything as an Object
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}Aggregating and Re-exporting Modules
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");Dynamic Module Importing
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});ES Modules in the Browser
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>Benefits of a Modular Approach
Organizing applications with ES Modules offers many benefits:
- Encapsulation - each module has its own namespace, preventing naming conflicts
- Reusability - modules can be easily reused in different parts of the application
- Organization - code is easier to understand and maintain when split into logical parts
- Team collaboration - different team members can work on separate modules
- Lazy loading - modules can be loaded only when they are needed
- Better dependency management - clearly defined dependencies between modules
Practical Example: Dinosaur Monitoring System
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});Browser Support and Webpack Configuration
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.
Basic Webpack Configuration for ES Modules
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};Best Practices for Working with Modules
- Single responsibility - each module should have one clearly defined responsibility
- Small modules - keep modules small and focused on a specific task
- Consistent naming - use consistent naming conventions for module files
- Explicit dependencies - all dependencies should be clearly defined in imports
- Minimize state mutation - avoid modifying global state in modules
- Documentation - add JSDoc comments to exported functions and classes
- Folder structure - organize modules into a logical folder structure (e.g. models, services, utils)
Summary
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.
Code for this lesson: main.js
1// ES Modules in Jurassic Park
2// Main park management system file
3
4console.log("Jurassic Park Management System");
5console.log("ES Modules demo (import/export)\n");
6
7// ===========================================
8// Simulated imports (in real ES6 these would be separate files)
9// ===========================================
10
11// === dinosaur.js - Dinosaur model ===
12class Dinosaur {
13 constructor(name, species, diet, dangerLevel) {
14 this.name = name;
15 this.species = species;
16 this.diet = diet;
17 this.dangerLevel = dangerLevel; // 1-10
18 this.isActive = true;
19 this.lastFed = new Date();
20 this.energy = 100;
21 }
22
23 feed() {
24 this.energy = Math.min(100, this.energy + 20);
25 this.lastFed = new Date();
26 return `${this.name} has been fed. Energy: ${this.energy}%`;
27 }
28
29 roar() {
30 const sounds = {
31 'carnivore': 'ROAAAAR!',
32 'herbivore': 'Hoooonk!',
33 'omnivore': 'Grrrrooo!'
34 };
35 return `${this.name}: ${sounds[this.diet] || 'Hmm?'}`;
36 }
37
38 getInfo() {
39 return {
40 name: this.name,
41 species: this.species,
42 diet: this.diet,
43 dangerLevel: this.dangerLevel,
44 energy: this.energy,
45 status: this.isActive ? 'Active' : 'Sleeping'
46 };
47 }
48}
49
50// Default export
51const DinosaurClass = Dinosaur;
52
53// === security.js - Security system ===
54const SECURITY_LEVELS = {
55 GREEN: 1,
56 YELLOW: 2,
57 ORANGE: 3,
58 RED: 4,
59 CRITICAL: 5
60};
61
62function calculateSecurityLevel(dinosaurs) {
63 const maxDanger = Math.max(...dinosaurs.map(d => d.dangerLevel));
64 const activeCarnivores = dinosaurs.filter(d => d.diet === 'carnivore' && d.isActive).length;
65
66 if (maxDanger >= 9 || activeCarnivores >= 3) return SECURITY_LEVELS.CRITICAL;
67 if (maxDanger >= 7 || activeCarnivores >= 2) return SECURITY_LEVELS.RED;
68 if (maxDanger >= 5 || activeCarnivores >= 1) return SECURITY_LEVELS.ORANGE;
69 if (maxDanger >= 3) return SECURITY_LEVELS.YELLOW;
70 return SECURITY_LEVELS.GREEN;
71}
72
73function getSecurityMessage(level) {
74 const messages = {
75 [SECURITY_LEVELS.GREEN]: "Park safe",
76 [SECURITY_LEVELS.YELLOW]: "Elevated vigilance",
77 [SECURITY_LEVELS.ORANGE]: "Medium threat",
78 [SECURITY_LEVELS.RED]: "High threat",
79 [SECURITY_LEVELS.CRITICAL]: "CRITICAL CONDITION"
80 };
81 return messages[level] || "Unknown level";
82}
83
84// Named exports
85const SecurityModule = {
86 SECURITY_LEVELS,
87 calculateSecurityLevel,
88 getSecurityMessage
89};
90
91// === enclosure.js - Paddock management ===
92class Enclosure {
93 constructor(id, name, capacity, type) {
94 this.id = id;
95 this.name = name;
96 this.capacity = capacity;
97 this.type = type; // 'carnivore', 'herbivore', 'mixed'
98 this.dinosaurs = [];
99 this.isLocked = true;
100 this.maintenanceMode = false;
101 }
102
103 addDinosaur(dinosaur) {
104 if (this.dinosaurs.length >= this.capacity) {
105 return `Error: Paddock ${this.name} is full`;
106 }
107
108 if (this.type === 'carnivore' && dinosaur.diet !== 'carnivore') {
109 return `Error: Paddock for carnivores cannot accept ${dinosaur.diet}`;
110 }
111
112 if (this.type === 'herbivore' && dinosaur.diet === 'carnivore') {
113 return `Error: Paddock for herbivores cannot accept carnivores`;
114 }
115
116 this.dinosaurs.push(dinosaur);
117 return `${dinosaur.name} added to paddock ${this.name}`;
118 }
119
120 removeDinosaur(dinosaurName) {
121 const index = this.dinosaurs.findIndex(d => d.name === dinosaurName);
122 if (index === -1) {
123 return `Error: Dinosaur ${dinosaurName} is not in this paddock`;
124 }
125
126 const removed = this.dinosaurs.splice(index, 1)[0];
127 return `${removed.name} removed from paddock ${this.name}`;
128 }
129
130 feedAll() {
131 const results = this.dinosaurs.map(dinosaur => dinosaur.feed());
132 return results;
133 }
134
135 getStatus() {
136 return {
137 id: this.id,
138 name: this.name,
139 capacity: this.capacity,
140 occupied: this.dinosaurs.length,
141 type: this.type,
142 isLocked: this.isLocked,
143 maintenanceMode: this.maintenanceMode,
144 dinosaurs: this.dinosaurs.map(d => d.getInfo())
145 };
146 }
147
148 lock() {
149 this.isLocked = true;
150 return `Paddock ${this.name} closed`;
151 }
152
153 unlock() {
154 this.isLocked = false;
155 return `Paddock ${this.name} opened`;
156 }
157}
158
159const EnclosureClass = Enclosure;
160
161// === utils.js - Helper functions ===
162function formatTime(date) {
163 return date.toLocaleTimeString('en-US');
164}
165
166function generateId() {
167 return 'ID-' + Math.random().toString(36).substr(2, 9).toUpperCase();
168}
169
170function logAction(action, details = '') {
171 const timestamp = formatTime(new Date());
172 const logEntry = `[${timestamp}] ${action} ${details}`;
173 console.log(logEntry);
174 return logEntry;
175}
176
177function calculateDistance(point1, point2) {
178 const dx = point2.x - point1.x;
179 const dy = point2.y - point1.y;
180 return Math.sqrt(dx * dx + dy * dy);
181}
182
183// Exporting many functions
184const UtilsModule = {
185 formatTime,
186 generateId,
187 logAction,
188 calculateDistance
189};
190
191// === park-manager.js - Main management system ===
192class ParkManager {
193 constructor() {
194 this.enclosures = new Map();
195 this.dinosaurs = new Map();
196 this.securityLevel = SecurityModule.SECURITY_LEVELS.GREEN;
197 this.isOpen = false;
198 this.visitors = 0;
199 }
200
201 // Paddock management
202 createEnclosure(name, capacity, type) {
203 const id = UtilsModule.generateId();
204 const enclosure = new EnclosureClass(id, name, capacity, type);
205 this.enclosures.set(id, enclosure);
206
207 UtilsModule.logAction('ENCLOSURE_CREATED', `${name} (capacity: ${capacity}, type: ${type})`);
208 return enclosure;
209 }
210
211 // Dinosaur management
212 addDinosaur(name, species, diet, dangerLevel, enclosureId) {
213 const dinosaur = new DinosaurClass(name, species, diet, dangerLevel);
214 this.dinosaurs.set(name, dinosaur);
215
216 if (enclosureId && this.enclosures.has(enclosureId)) {
217 const enclosure = this.enclosures.get(enclosureId);
218 const result = enclosure.addDinosaur(dinosaur);
219 UtilsModule.logAction('DINOSAUR_ADDED', `${name} -> ${enclosure.name}`);
220 console.log(result);
221 }
222
223 this.updateSecurityLevel();
224 return dinosaur;
225 }
226
227 // Security level update
228 updateSecurityLevel() {
229 const allDinosaurs = Array.from(this.dinosaurs.values());
230 this.securityLevel = SecurityModule.calculateSecurityLevel(allDinosaurs);
231
232 const message = SecurityModule.getSecurityMessage(this.securityLevel);
233 UtilsModule.logAction('SECURITY_UPDATE', message);
234 }
235
236 // Park status report
237 getParkStatus() {
238 const enclosureStatuses = Array.from(this.enclosures.values()).map(e => e.getStatus());
239 const dinosaurCount = this.dinosaurs.size;
240 const securityMessage = SecurityModule.getSecurityMessage(this.securityLevel);
241
242 return {
243 isOpen: this.isOpen,
244 visitors: this.visitors,
245 dinosaurCount,
246 enclosureCount: this.enclosures.size,
247 securityLevel: this.securityLevel,
248 securityMessage,
249 enclosures: enclosureStatuses,
250 timestamp: UtilsModule.formatTime(new Date())
251 };
252 }
253
254 // Opening/closing the park
255 openPark() {
256 if (this.securityLevel <= SecurityModule.SECURITY_LEVELS.YELLOW) {
257 this.isOpen = true;
258 UtilsModule.logAction('PARK_OPENED', 'Park open for visitors');
259 return "Jurassic Park is open!";
260 } else {
261 return "Park cannot be opened - danger level too high";
262 }
263 }
264
265 closePark() {
266 this.isOpen = false;
267 this.visitors = 0;
268 UtilsModule.logAction('PARK_CLOSED', 'Park closed');
269 return "Jurassic Park closed";
270 }
271
272 // Feeding all dinosaurs
273 feedAllDinosaurs() {
274 const results = [];
275
276 for (const enclosure of this.enclosures.values()) {
277 const feedResults = enclosure.feedAll();
278 results.push(...feedResults);
279 }
280
281 UtilsModule.logAction('MASS_FEEDING', `Fed ${results.length} dinosaurs`);
282 return results;
283 }
284
285 // Emergency
286 emergencyProtocol() {
287 UtilsModule.logAction('EMERGENCY', 'Emergency protocol activated!');
288
289 // Close park
290 this.closePark();
291
292 // Close all paddocks
293 for (const enclosure of this.enclosures.values()) {
294 enclosure.lock();
295 }
296
297 console.log("EMERGENCY PROTOCOL ACTIVATED!");
298 console.log("- Park closed");
299 console.log("- All paddocks locked");
300 console.log("- Guest evacuation in progress");
301
302 return "Emergency protocol activated";
303 }
304}
305
306// ===========================================
307// SYSTEM DEMO
308// ===========================================
309console.log("=== PARK SYSTEM INITIALIZATION ===\n");
310
311// Creating the park manager
312const parkManager = new ParkManager();
313
314// Creating paddocks
315console.log("Creating paddocks...");
316const carnivorePaddock = parkManager.createEnclosure("Paddock A - Carnivores", 3, "carnivore");
317const herbivorePaddock = parkManager.createEnclosure("Paddock B - Herbivores", 5, "herbivore");
318const mixedPaddock = parkManager.createEnclosure("Paddock C - Mixed", 4, "mixed");
319
320console.log("\nAdding dinosaurs...");
321
322// Adding dinosaurs
323const trex = parkManager.addDinosaur(
324 "Rexy",
325 "Tyrannosaurus Rex",
326 "carnivore",
327 9,
328 carnivorePaddock.id
329);
330
331const raptor1 = parkManager.addDinosaur(
332 "Blue",
333 "Velociraptor",
334 "carnivore",
335 7,
336 carnivorePaddock.id
337);
338
339const raptor2 = parkManager.addDinosaur(
340 "Charlie",
341 "Velociraptor",
342 "carnivore",
343 7,
344 carnivorePaddock.id
345);
346
347const trice = parkManager.addDinosaur(
348 "Spike",
349 "Triceratops",
350 "herbivore",
351 3,
352 herbivorePaddock.id
353);
354
355const brachi = parkManager.addDinosaur(
356 "Gentle Giant",
357 "Brachiosaurus",
358 "herbivore",
359 2,
360 herbivorePaddock.id
361);
362
363const stego = parkManager.addDinosaur(
364 "Spike Jr",
365 "Stegosaurus",
366 "herbivore",
367 4,
368 mixedPaddock.id
369);
370
371// Testing dinosaur sounds
372console.log("\nCommunication test with the dinosaurs:");
373console.log(trex.roar());
374console.log(raptor1.roar());
375console.log(trice.roar());
376console.log(brachi.roar());
377
378// Park status report
379console.log("\nPARK STATUS REPORT:");
380const status = parkManager.getParkStatus();
381console.log(`Status: ${status.isOpen ? 'Open' : 'Closed'}`);
382console.log(`Dinosaurs: ${status.dinosaurCount}`);
383console.log(`Paddocks: ${status.enclosureCount}`);
384console.log(`Security: ${status.securityMessage}`);
385
386// Attempting to open park
387console.log("\nAttempting to open park...");
388const openResult = parkManager.openPark();
389console.log(openResult);
390
391// Feeding dinosaurs
392console.log("\nFeeding dinosaurs...");
393const feedingResults = parkManager.feedAllDinosaurs();
394feedingResults.forEach(result => console.log(" " + result));
395
396// Emergency simulation
397console.log("\nSimulating a failure...");
398setTimeout(() => {
399 console.log("\nDANGER DETECTED!");
400 parkManager.emergencyProtocol();
401
402 // Status after the failure
403 setTimeout(() => {
404 console.log("\nStatus after the emergency protocol:");
405 const finalStatus = parkManager.getParkStatus();
406 console.log(`Park: ${finalStatus.isOpen ? 'Open' : 'Closed'}`);
407 console.log(`Security: ${finalStatus.securityMessage}`);
408
409 console.log("\nPaddock details:");
410 finalStatus.enclosures.forEach(enc => {
411 console.log(` ${enc.name}: ${enc.occupied}/${enc.capacity} dinosaurs, ${enc.isLocked ? 'Closed' : 'Open'}`);
412 });
413 }, 2000);
414}, 3000);
415
416// ===========================================
417// DEMO OF DIFFERENT EXPORT TYPES
418// ===========================================
419console.log("\n=== ES6 EXPORT TYPES ===");
420
421// 1. Default export - one main thing from a module
422console.log("1. Default export - main Dinosaur class");
423const myDino = new DinosaurClass("Test Dino", "Test Species", "omnivore", 5);
424console.log(" Created:", myDino.name);
425
426// 2. Named exports - many functions/objects
427console.log("\n2. Named exports - utility functions");
428console.log(" Generated ID:", UtilsModule.generateId());
429console.log(" Current time:", UtilsModule.formatTime(new Date()));
430
431// 3. Re-exports - exporting from other modules
432console.log("\n3. Security module exports");
433console.log(" Security levels:", Object.keys(SecurityModule.SECURITY_LEVELS));
434
435// 4. Namespace import - everything as one object
436console.log("\n4. Combined module usage:");
437const testEnclosure = new EnclosureClass("TEST-001", "Test Enclosure", 2, "mixed");
438testEnclosure.addDinosaur(myDino);
439console.log(" Test enclosure created with:", testEnclosure.dinosaurs.length, "dinosaur(s)");
440
441// ===========================================
442// SIMULATION OF REAL ES6 IMPORTS
443// ===========================================
444console.log("\n=== SIMULATION OF ES6 IMPORTS ===");
445console.log("In a real application we would use:");
446console.log("");
447console.log("// main.js");
448console.log("import Dinosaur from './dinosaur.js'; // default import");
449console.log("import { SECURITY_LEVELS, calculateSecurityLevel } from './security.js'; // named imports");
450console.log("import * as Utils from './utils.js'; // namespace import");
451console.log("import Enclosure, { EnclosureType } from './enclosure.js'; // mixed import");
452console.log("");
453console.log("// dinosaur.js");
454console.log("export default class Dinosaur { ... }");
455console.log("");
456console.log("// security.js");
457console.log("export const SECURITY_LEVELS = { ... };");
458console.log("export function calculateSecurityLevel(dinosaurs) { ... }");
459console.log("");
460console.log("// utils.js");
461console.log("export function formatTime(date) { ... }");
462console.log("export function generateId() { ... }");
463console.log("export { formatTime as timeFormatter }; // alias export");
464
465console.log("\nES Modules demo completed!");Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What is a module in JavaScript?
Hands-on tasks in the game
- Horizontal ordering
Arrange the module import elements: