Witaj z powrotem w Parku Jurajskim! Po zdobyciu solidnych podstaw JavaScript i poznaniu technik asynchronicznych, czas na poznanie sposobów organizacji kodu w większe, skalowalne projekty. W świecie tak złożonym jak Park Jurajski, potrzebujemy dobrze zorganizowanej struktury, aby zapewnić bezpieczeństwo i efektywność systemu.
Wyobraź sobie, że zarządzasz Parkiem Jurajskim bez podziału na zespoły i działy - wszystko byłoby chaotyczne i niebezpieczne! Podobnie jest z kodem - gdy wszystko znajduje się w jednym pliku, szybko staje się to nieczytelne i trudne w utrzymaniu.
ES Modules (ECMAScript Modules) to standardowy system modułów w JavaScript, który pozwala na:
Zacznijmy od stworzenia kilku podstawowych modułów dla naszego systemu zarządzania Parkiem Jurajskim:
Gdy mamy jeden główny element do wyeksportowania, używamy eksportu domyślnego:
1// dinosaur.js - Model dinozaura
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} został nakarmiony. Poziom zdrowia: ${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 ? "WYSOKI POZIOM ZAGROŻENIA" : ""
23 };
24 }
25}
26
27// Eksport domyślny - tylko jedna klasa/funkcja/obiekt per plik
28export default Dinosaur;Gdy mamy wiele elementów do wyeksportowania, używamy eksportów nazwanych:
1// securityUtils.js - Narzędzia do obsługi bezpieczeństwa
2export const SECURITY_LEVELS = {
3 GREEN: "normalny",
4 YELLOW: "podwyższony",
5 ORANGE: "wysoki",
6 RED: "krytyczny"
7};
8
9export function checkFenceStatus(sectorId) {
10 // Symulacja sprawdzania statusu ogrodzenia w danym sektorze
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! Sektor ${sector}: ${reason}`);
21 // Logika uruchamiania alarmu
22 return {
23 success: true,
24 alarmId: "ALR-" + Math.floor(Math.random() * 10000)
25 };
26}
27
28// Możemy też eksportować funkcję domyślną obok nazwanych eksportów
29export default function getSecurityStatus() {
30 // Zwraca ogólny status bezpieczeństwa parku
31 return {
32 level: SECURITY_LEVELS.GREEN,
33 activeAlarms: 0,
34 lastChecked: new Date().toISOString()
35 };
36}Możemy też najpierw zdefiniować nasze zmienne, funkcje lub klasy, a następnie wyeksportować je na końcu pliku:
1// parkStats.js - Statystyki Parku Jurajskiego
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// Eksport wielu elementów naraz
37export {
38 addVisitors,
39 addRevenue,
40 recordIncident,
41 getDailyStats
42};
43
44// Możemy również zmienić nazwę eksportowanego elementu
45export { recordIncident as logParkIncident };Po utworzeniu modułów, czas je wykorzystać w głównej aplikacji:
1// app.js
2import Dinosaur from './dinosaur.js';
3
4// Tworzymy nowe instancje dinozaurów
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// Używamy importowanych funkcji i stałych
5function monitorSecurity() {
6 const sectors = ["A1", "B2", "C3", "D4"];
7
8 // Sprawdzamy każdy sektor
9 sectors.forEach(sector => {
10 const fenceStatus = checkFenceStatus(sector);
11
12 if (fenceStatus.status === "failure") {
13 const alarmResult = triggerAlarm(
14 sector,
15 "Awaria ogrodzenia elektrycznego!"
16 );
17 console.log(`Alarm ${alarmResult.alarmId} uruchomiony w sektorze ${sector}`);
18
19 document.querySelector("#securityStatus").textContent = SECURITY_LEVELS.RED;
20 }
21 });
22}
23
24setInterval(monitorSecurity, 60000); // Sprawdzanie co minutę1// dashboard.js
2import getSecurityStatus, { SECURITY_LEVELS, checkFenceStatus } from './securityUtils.js';
3
4// Używamy domyślnego importu
5const parkStatus = getSecurityStatus();
6console.log("Status bezpieczeństwa:", parkStatus.level);
7
8// Używamy nazwanych importów
9if (parkStatus.level === SECURITY_LEVELS.RED) {
10 // Sprawdź wszystkie sektory natychmiast
11 ["A1", "B2", "C3", "D4"].forEach(sector => {
12 console.log(`Sprawdzanie sektora ${sector}: `, checkFenceStatus(sector));
13 });
14}1// incidentReport.js
2import { recordIncident as logIncident } from './parkStats.js';
3
4// Używamy zaimportowanej funkcji pod nową nazwą
5function reportEmergency(location, details) {
6 logIncident("EMERGENCY", location, details);
7 // Dodatkowa logika obsługi sytuacji awaryjnej
8}1// adminPanel.js
2import * as ParkStats from './parkStats.js';
3
4// Dostęp do wszystkich funkcji z modułu przez obiekt
5function generateDailyReport() {
6 const visitorCount = ParkStats.addVisitors(120); // Dodaje 120 odwiedzających
7 const dailyRevenue = ParkStats.addRevenue(15000); // Dodaje $15,000 przychodu
8
9 const stats = ParkStats.getDailyStats();
10
11 return `
12 Dzienny raport Parku Jurajskiego:
13 - Liczba odwiedzających: ${stats.totalVisitors}
14 - Przychód: $${stats.revenue}
15 - Incydenty: ${stats.incidentCount}
16 `;
17}W dużych projektach często warto stworzyć punkty wejścia, które agregują i re-eksportują funkcjonalności z wielu mniejszych modułów:
1// models/index.js - Agregacja wszystkich modeli
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';Dzięki temu możemy importować wszystkie modele z jednego punktu wejścia:
1// main.js
2import { Dinosaur, Employee, Facility } from './models/index.js';
3
4// Teraz możemy korzystać z wszystkich modeli
5const rex = new Dinosaur("Rex", "Tyrannosaurus", "carnivore", 9);
6const lab = new Facility("Laboratoria InGen", "B3", 12);
7const scientist = new Employee("Dr. Wu", "Główny genetyk", "lab");ES Modules oferują również mechanizm dynamicznego importowania, co pozwala na ładowanie modułów na żądanie:
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 // Wykonaj procedurę awaryjną z załadowanego modułu
20 emergencyModule.initiateProtocol();
21}
22
23// Przycisk w UI do uruchomienia procedury awaryjnej
24document.querySelector("#emergencyButton").addEventListener("click", () => {
25 const emergencyType = document.querySelector("#emergencyType").value;
26 handleEmergency(emergencyType);
27});Aby używać ES Modules bezpośrednio w przeglądarce, musimy dodać atrybut
type="module" do tagu script:1<!DOCTYPE html>
2<html>
3<head>
4 <title>Panel Kontrolny Parku Jurajskiego</title>
5</head>
6<body>
7 <h1>Panel Kontrolny Parku Jurajskiego</h1>
8 <div id="securityStatus"></div>
9 <select id="emergencyType">
10 <option value="dinosaurEscape">Ucieczka dinozaura</option>
11 <option value="powerOutage">Awaria zasilania</option>
12 <option value="weatherAlert">Alert pogodowy</option>
13 </select>
14 <button id="emergencyButton">Uruchom procedurę awaryjną</button>
15
16 <!-- Główny skrypt używający modułów -->
17 <script type="module" src="./app.js"></script>
18</body>
19</html>Organizowanie aplikacji z wykorzystaniem ES Modules oferuje wiele korzyści:
Zobaczmy, jak moglibyśmy zorganizować większy system monitorowania dinozaurów w Parku Jurajskim z wykorzystaniem modułów:
1// models/dinosaur.js
2export default class Dinosaur {
3 // ... implementacja klasy Dinosaur (jak wcześniej)
4}
5
6// services/dinoTracking.js
7import Dinosaur from '../models/dinosaur.js';
8
9export async function getAllDinosaurs() {
10 // Pobieranie danych o dinozaurach z API
11 const response = await fetch('/api/dinosaurs');
12 const data = await response.json();
13
14 // Konwersja surowych danych na instancje klasy Dinosaur
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 // Pobieranie danych o aktywnych ogrodzeniach
31 const response = await fetch('/api/fences/active');
32 return response.json();
33}
34
35export async function activateSectorLockdown(sectorId) {
36 // Uruchamianie blokady w danym sektorze
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 // Usunięcie alertu po 5 sekundach (chyba że jest krytyczny)
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 // Pobierz dane o dinozaurach i statusie ogrodzeń
74 const [allDinos, activeFences] = await Promise.all([
75 getAllDinosaurs(),
76 getActiveFences()
77 ]);
78
79 // Znajdź niebezpieczne dinozaury
80 const dangerousDinos = await getDinosaursByDangerLevel(8);
81
82 // Wyświetl dane na dashboardzie
83 renderDinosaurTable(allDinos);
84 renderSecurityStatus(activeFences);
85
86 // Pokaż alerty dla niebezpiecznych dinozaurów
87 if (dangerousDinos.length > 0) {
88 showAlert(
89 `UWAGA: ${dangerousDinos.length} dinozaurów z wysokim poziomem zagrożenia`,
90 ALERT_LEVELS.WARNING
91 );
92 }
93
94 // Ustaw nasłuchiwanie na zdarzenia awaryjne
95 setupEmergencyHandlers();
96
97 } catch (error) {
98 showAlert(`Błąd inicjalizacji dashboardu: ${error.message}`, ALERT_LEVELS.DANGER);
99 }
100}
101
102function renderDinosaurTable(dinosaurs) {
103 // Logika renderowania tabeli dinozaurów
104}
105
106function renderSecurityStatus(fences) {
107 // Logika renderowania statusu zabezpieczeń
108}
109
110function setupEmergencyHandlers() {
111 // Logika ustawiania obsługi zdarzeń awaryjnych
112 document.querySelector('#emergencyLockdown').addEventListener('click', async () => {
113 const sector = document.querySelector('#sectorSelect').value;
114 try {
115 await activateSectorLockdown(sector);
116 showAlert(`Blokada sektora ${sector} aktywowana`, ALERT_LEVELS.WARNING);
117 } catch (error) {
118 showAlert(`Nie udało się aktywować blokady: ${error.message}`, ALERT_LEVELS.CRITICAL);
119 }
120 });
121}
122
123// app.js - Główny plik aplikacji
124import { initializeDashboard } from './controllers/monitoringDashboard.js';
125
126// Inicjalizacja aplikacji gdy DOM jest gotowy
127document.addEventListener('DOMContentLoaded', () => {
128 initializeDashboard();
129
130 // Dodatkowa logika inicjalizacyjna
131 console.log('System monitorowania Parku Jurajskiego uruchomiony');
132});Nowsze przeglądarki obsługują ES Modules natywnie, ale dla pełnej kompatybilności z starszymi przeglądarkami często używamy narzędzi takich jak Webpack lub Rollup, które bundlują nasze moduły w kod kompatybilny z starszymi przeglądarkami.
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 to potężny mechanizm organizacji kodu w JavaScript, który pozwala na tworzenie modularnych, dobrze zorganizowanych aplikacji. W świecie tak złożonym jak Park Jurajski, gdzie każdy błąd może mieć katastrofalne konsekwencje, dobra organizacja kodu jest kluczowa dla utrzymania bezpieczeństwa i wydajności.
W następnym ćwiczeniu przyjrzymy się alternatywnemu systemowi modułów - CommonJS, który jest szeroko używany w środowisku Node.js, idealnym do tworzenia serwerowej części naszego systemu zarządzania Parkiem Jurajskim.