JavaScript and TypeScript course Β· Module 5: Advanced JavaScript

ES Modules (import/export)

10 min read
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 minute

3. 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:

  1. Encapsulation - each module has its own namespace, preventing naming conflicts
  2. Reusability - modules can be easily reused in different parts of the application
  3. Organization - code is easier to understand and maintain when split into logical parts
  4. Team collaboration - different team members can work on separate modules
  5. Lazy loading - modules can be loaded only when they are needed
  6. 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

  1. Single responsibility - each module should have one clearly defined responsibility
  2. Small modules - keep modules small and focused on a specific task
  3. Consistent naming - use consistent naming conventions for module files
  4. Explicit dependencies - all dependencies should be clearly defined in imports
  5. Minimize state mutation - avoid modifying global state in modules
  6. Documentation - add JSDoc comments to exported functions and classes
  7. 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. 1. What is a module in JavaScript?

Hands-on tasks in the game

  • Horizontal ordering

    Arrange the module import elements:

Useful articles