Kurs JavaScript i TypeScript · Moduł 5: Zaawansowany JavaScript

ES Modules (import/export)

9 min czytania
W tej lekcji11

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.

Modularyzacja kodu w JavaScript

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:

  • Dzielenie kodu na mniejsze, łatwiejsze w zarządzaniu części
  • Kontrolowanie, które elementy są dostępne na zewnątrz modułu
  • Definiowanie zależności między różnymi częściami programu

Eksportowanie elementów z modułów

Zacznijmy od stworzenia kilku podstawowych modułów dla naszego systemu zarządzania Parkiem Jurajskim:

1. Eksport domyślny (default export)

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;

2. Eksport nazwany (named exports)

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}

3. Eksportowanie wcześniej zdefiniowanych zmiennych

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 };

Importowanie elementów z modułów

Po utworzeniu modułów, czas je wykorzystać w głównej aplikacji:

1. Import domyślny

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());

2. Import nazwany

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ę

3. Import domyślny i nazwany razem

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}

4. Import ze zmianą nazwy

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}

5. Import wszystkiego jako obiekt

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}

Agregacja i re-eksportowanie modułów

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");

Dynamiczne importowanie modułów

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});

Specyfika ES Modules w przeglądarce

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>

Zalety modularnego podejścia

Organizowanie aplikacji z wykorzystaniem ES Modules oferuje wiele korzyści:

  1. Enkapsulacja - każdy moduł ma własną przestrzeń nazw, co zapobiega konfliktom nazwowym
  2. Reużywalność - możemy łatwo ponownie używać modułów w różnych częściach aplikacji
  3. Organizacja - łatwiej jest zrozumieć i utrzymać kod, gdy jest podzielony na logiczne części
  4. Współpraca w zespole - różni członkowie zespołu mogą pracować nad odrębnymi modułami
  5. Lazy loading - możemy ładować moduły tylko wtedy, gdy są potrzebne
  6. Lepsze zarządzanie zależnościami - jasno zdefiniowane zależności między modułami

Praktyczny przykład: System monitorowania dinozaurów

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});

Wsparcie przeglądarek i konfiguracja Webpack

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.

Podstawowa konfiguracja Webpack dla 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};

Dobre praktyki przy pracy z modułami

  1. Jedna odpowiedzialność - każdy moduł powinien mieć jedną, jasno określoną odpowiedzialność
  2. Małe moduły - staraj się, aby moduły były małe i skupione na określonym zadaniu
  3. Spójne nazewnictwo - używaj spójnych konwencji nazewnictwa dla plików modułów
  4. Jawne zależności - wszystkie zależności powinny być jasno zdefiniowane w importach
  5. Minimalizacja modyfikacji stanu - unikaj modyfikowania stanu globalnego w modułach
  6. Dokumentacja - dodawaj komentarze JSDoc do eksportowanych funkcji i klas
  7. Struktura folderów - organizuj moduły w logiczną strukturę folderów (np. models, services, utils)

Podsumowanie

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.

Kod do tej lekcji: main.js
1// ES Modules w Parku Jurajskim
2// Główny plik systemu zarządzania parkiem
3
4console.log("System Zarządzania Parkiem Jurajskim");
5console.log("Demonstracja ES Modules (import/export)\n");
6
7// ===========================================
8// Symulacja importów (w prawdziwym ES6 byłyby to oddzielne pliki)
9// ===========================================
10
11// === dinosaur.js - Model dinozaura ===
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} został nakarmiony. Energia: ${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// Eksport domyślny (default export)
51const DinosaurClass = Dinosaur;
52
53// === security.js - System bezpieczeństwa ===
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 bezpieczny",
76    [SECURITY_LEVELS.YELLOW]: "Podwyższona czujność",
77    [SECURITY_LEVELS.ORANGE]: "Zagrożenie średnie",
78    [SECURITY_LEVELS.RED]: "Wysokie zagrożenie",
79    [SECURITY_LEVELS.CRITICAL]: "STAN KRYTYCZNY"
80  };
81  return messages[level] || "Nieznany poziom";
82}
83
84// Eksport nazwane (named exports)
85const SecurityModule = {
86  SECURITY_LEVELS,
87  calculateSecurityLevel,
88  getSecurityMessage
89};
90
91// === enclosure.js - Zarządzanie wybiegami ===
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 `Błąd: Wybieg ${this.name} jest pełny`;
106    }
107
108    if (this.type === 'carnivore' && dinosaur.diet !== 'carnivore') {
109      return `Błąd: Wybieg dla mięsożerców nie może przyjąć ${dinosaur.diet}`;
110    }
111
112    if (this.type === 'herbivore' && dinosaur.diet === 'carnivore') {
113      return `Błąd: Wybieg dla roślinożerców nie może przyjąć mięsożerców`;
114    }
115
116    this.dinosaurs.push(dinosaur);
117    return `${dinosaur.name} dodany do wybiegu ${this.name}`;
118  }
119
120  removeDinosaur(dinosaurName) {
121    const index = this.dinosaurs.findIndex(d => d.name === dinosaurName);
122    if (index === -1) {
123      return `Błąd: Dinozaur ${dinosaurName} nie znajduje się w tym wybiegu`;
124    }
125
126    const removed = this.dinosaurs.splice(index, 1)[0];
127    return `${removed.name} usunięty z wybiegu ${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 `Wybieg ${this.name} zamknięty`;
151  }
152
153  unlock() {
154    this.isLocked = false;
155    return `Wybieg ${this.name} otwarty`;
156  }
157}
158
159const EnclosureClass = Enclosure;
160
161// === utils.js - Funkcje pomocnicze ===
162function formatTime(date) {
163  return date.toLocaleTimeString('pl-PL');
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// Eksport wielu funkcji
184const UtilsModule = {
185  formatTime,
186  generateId,
187  logAction,
188  calculateDistance
189};
190
191// === park-manager.js - Główny system zarządzania ===
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  // Zarządzanie wybiegami
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  // Zarządzanie dinozaurami
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  // Aktualizacja poziomu bezpieczeństwa
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  // Raport stanu parku
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  // Otwarcie/zamknięcie parku
255  openPark() {
256    if (this.securityLevel <= SecurityModule.SECURITY_LEVELS.YELLOW) {
257      this.isOpen = true;
258      UtilsModule.logAction('PARK_OPENED', 'Park otwarty dla zwiedzających');
259      return "Park Jurajski otwarty!";
260    } else {
261      return "Park nie może być otwarty - zbyt wysokie zagrożenie";
262    }
263  }
264
265  closePark() {
266    this.isOpen = false;
267    this.visitors = 0;
268    UtilsModule.logAction('PARK_CLOSED', 'Park zamknięty');
269    return "Park Jurajski zamknięty";
270  }
271
272  // Karmienie wszystkich dinozaurów
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', `Nakarmiono ${results.length} dinozaurów`);
282    return results;
283  }
284
285  // Sytuacja awaryjna
286  emergencyProtocol() {
287    UtilsModule.logAction('EMERGENCY', 'Aktywowano protokół awaryjny!');
288
289    // Zamknij park
290    this.closePark();
291
292    // Zamknij wszystkie wybiegi
293    for (const enclosure of this.enclosures.values()) {
294      enclosure.lock();
295    }
296
297    console.log("PROTOKÓŁ AWARYJNY AKTYWOWANY!");
298    console.log("- Park zamknięty");
299    console.log("- Wszystkie wybiegi zablokowane");
300    console.log("- Ewakuacja gości w toku");
301
302    return "Protokół awaryjny aktywowany";
303  }
304}
305
306// ===========================================
307// DEMONSTRACJA SYSTEMU
308// ===========================================
309console.log("=== INICJALIZACJA SYSTEMU PARKU ===\n");
310
311// Tworzenie managera parku
312const parkManager = new ParkManager();
313
314// Tworzenie wybiegów
315console.log("Tworzenie wybiegów...");
316const carnivorePaddock = parkManager.createEnclosure("Paddock A - Mięsożercy", 3, "carnivore");
317const herbivorePaddock = parkManager.createEnclosure("Paddock B - Roślinożercy", 5, "herbivore");
318const mixedPaddock = parkManager.createEnclosure("Paddock C - Mieszany", 4, "mixed");
319
320console.log("\nDodawanie dinozaurów...");
321
322// Dodawanie dinozaurów
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// Test dźwięków dinozaurów
372console.log("\nTest komunikacji z dinozaurami:");
373console.log(trex.roar());
374console.log(raptor1.roar());
375console.log(trice.roar());
376console.log(brachi.roar());
377
378// Raport stanu parku
379console.log("\nRAPORT STANU PARKU:");
380const status = parkManager.getParkStatus();
381console.log(`Status: ${status.isOpen ? 'Otwarty' : 'Zamknięty'}`);
382console.log(`Dinozaury: ${status.dinosaurCount}`);
383console.log(`Wybiegi: ${status.enclosureCount}`);
384console.log(`Bezpieczeństwo: ${status.securityMessage}`);
385
386// Próba otwarcia parku
387console.log("\nPróba otwarcia parku...");
388const openResult = parkManager.openPark();
389console.log(openResult);
390
391// Karmienie dinozaurów
392console.log("\nKarmienie dinozaurów...");
393const feedingResults = parkManager.feedAllDinosaurs();
394feedingResults.forEach(result => console.log("  " + result));
395
396// Symulacja sytuacji awaryjnej
397console.log("\nSymulacja awarii...");
398setTimeout(() => {
399  console.log("\nWYKRYTO NIEBEZPIECZEŃSTWO!");
400  parkManager.emergencyProtocol();
401
402  // Status po awarii
403  setTimeout(() => {
404    console.log("\nStatus po protokole awaryjnym:");
405    const finalStatus = parkManager.getParkStatus();
406    console.log(`Park: ${finalStatus.isOpen ? 'Otwarty' : 'Zamknięty'}`);
407    console.log(`Bezpieczeństwo: ${finalStatus.securityMessage}`);
408
409    console.log("\nSzczegóły wybiegów:");
410    finalStatus.enclosures.forEach(enc => {
411      console.log(`  ${enc.name}: ${enc.occupied}/${enc.capacity} dinozaurów, ${enc.isLocked ? 'Zamknięty' : 'Otwarty'}`);
412    });
413  }, 2000);
414}, 3000);
415
416// ===========================================
417// DEMONSTRACJA RÓŻNYCH TYPÓW EKSPORTÓW
418// ===========================================
419console.log("\n=== TYPY EKSPORTÓW ES6 ===");
420
421// 1. Default export - jedna główna rzecz z modułu
422console.log("1. Default export - główna klasa Dinosaur");
423const myDino = new DinosaurClass("Test Dino", "Test Species", "omnivore", 5);
424console.log("   Utworzono:", myDino.name);
425
426// 2. Named exports - wiele funkcji/obiektów
427console.log("\n2. Named exports - funkcje użytkowe");
428console.log("   Generated ID:", UtilsModule.generateId());
429console.log("   Current time:", UtilsModule.formatTime(new Date()));
430
431// 3. Re-exports - eksportowanie z innych modułów
432console.log("\n3. Security module exports");
433console.log("   Security levels:", Object.keys(SecurityModule.SECURITY_LEVELS));
434
435// 4. Namespace import - wszystko jako jeden obiekt
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// SYMULACJA RZECZYWISTYCH IMPORTÓW ES6
443// ===========================================
444console.log("\n=== SYMULACJA IMPORTÓW ES6 ===");
445console.log("W prawdziwej aplikacji używalibyśmy:");
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("\nDemonstracja ES Modules zakończona!");

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Czym jest moduł w JavaScript?

Zadania praktyczne w grze

  • Układanie w poziomie

    Ułóż elementy importu modułu:

Przydatne artykuły