Witaj ponownie w Parku Jurajskim! W poprzedniej lekcji poznaliśmy podstawy funkcji w JavaScript. Teraz zagłębimy się w to, jak funkcje mogą przyjmować dane i pracować z nimi - co jest kluczowe dla tworzenia elastycznych i wydajnych systemów zarządzania parkiem.
Choć terminy "parametry" i "argumenty" są często używane zamiennie, istnieje między nimi subtelna różnica:
1// name i species to parametry
2function addDinosaur(name, species) {
3 console.log(`Dodawanie nowego dinozaura: ${name} (${species})`);
4 // Kod dodający dinozaura do systemu
5}
6
7// "Blue" i "Velociraptor" to argumenty
8addDinosaur("Blue", "Velociraptor");JavaScript oferuje kilka sposobów pracy z parametrami funkcji:
W JavaScript wszystkie parametry są technicznie opcjonalne, ale możemy zaprojektować funkcje, które traktują niektóre parametry jako obowiązkowe:
1function feedDinosaur(dinoName, foodType) {
2 // Sprawdzenie czy parametry zostały podane
3 if (dinoName === undefined) {
4 throw new Error("Musisz podać nazwę dinozaura!");
5 }
6
7 // foodType jest opcjonalny, używamy wartości domyślnej jeśli nie został podany
8 const food = foodType || "mięso";
9
10 console.log(`Karmienie ${dinoName} używając ${food}`);
11 return `${dinoName} został nakarmiony`;
12}
13
14// Wywołania
15feedDinosaur("Rex", "żywe bydło"); // "Karmienie Rex używając żywe bydło"
16feedDinosaur("Blue"); // "Karmienie Blue używając mięso"
17// feedDinosaur(); // Error: Musisz podać nazwę dinozaura!Od ES6 (ES2015), JavaScript obsługuje parametry domyślne, które upraszczają definiowanie wartości domyślnych:
1function scheduleFeeding(dinoName, foodType = "mięso", time = "12:00") {
2 console.log(`Zaplanowano karmienie ${dinoName} używając ${foodType} o godzinie ${time}`);
3 return {
4 dino: dinoName,
5 food: foodType,
6 scheduledTime: time
7 };
8}
9
10// Wywołania
11scheduleFeeding("Triceratops", "rośliny", "10:30");
12// "Zaplanowano karmienie Triceratops używając rośliny o godzinie 10:30"
13
14scheduleFeeding("Raptor");
15// "Zaplanowano karmienie Raptor używając mięso o godzinie 12:00"
16
17scheduleFeeding("Rex", undefined, "15:45");
18// "Zaplanowano karmienie Rex używając mięso o godzinie 15:45"Parametry domyślne są oceniane w czasie wywołania funkcji, co oznacza, że możesz używać wyrażeń, a nawet innych parametrów:
1function prepareEnclosure(dinoType, securityLevel = calculateSecurityLevel(dinoType), capacity = securityLevel * 2) {
2 console.log(`Przygotowywanie zagrody dla ${dinoType}...`);
3 console.log(`Poziom zabezpieczeń: ${securityLevel}`);
4 console.log(`Pojemność: ${capacity} osobników`);
5}
6
7function calculateSecurityLevel(dinoType) {
8 const dangerousSpecies = ["Tyrannosaurus", "Velociraptor", "Spinosaurus"];
9 return dangerousSpecies.includes(dinoType) ? 5 : 3;
10}
11
12// Przygotowanie zagrody dla T-Rexa (wysoki poziom bezpieczeństwa)
13prepareEnclosure("Tyrannosaurus");
14// Poziom zabezpieczeń: 5, Pojemność: 10 osobników
15
16// Przygotowanie zagrody dla Triceratopsa (standardowy poziom bezpieczeństwa)
17prepareEnclosure("Triceratops");
18// Poziom zabezpieczeń: 3, Pojemność: 6 osobników
19
20// Przygotowanie niestandardowej zagrody
21prepareEnclosure("Velociraptor", 6, 4);
22// Poziom zabezpieczeń: 6, Pojemność: 4 osobnikówW systemach tak złożonych jak Park Jurajski, czasem potrzebujemy funkcji, które mogą przyjmować zmienną liczbę argumentów.
Każda funkcja w JavaScript ma dostęp do specjalnego obiektu
arguments, który zawiera wszystkie przekazane do niej argumenty:1function alertSecurityTeam() {
2 console.log(`Wysyłanie alertu do ${arguments.length} członków zespołu bezpieczeństwa.`);
3
4 for (let i = 0; i < arguments.length; i++) {
5 console.log(`Powiadamianie: ${arguments[i]}`);
6 }
7}
8
9// Możemy wywołać funkcję z dowolną liczbą argumentów
10alertSecurityTeam("John Hammond", "Robert Muldoon", "Ellie Sattler");
11// Wysyłanie alertu do 3 członków zespołu bezpieczeństwa.
12// Powiadamianie: John Hammond
13// Powiadamianie: Robert Muldoon
14// Powiadamianie: Ellie SattlerObiekt
arguments nie jest prawdziwą tablicą, ale podobnym do tablicy obiektem (array-like object). Można go konwertować do tablicy:1function logVisitors() {
2 // Konwersja arguments na tablicę
3 const visitorsArray = Array.from(arguments);
4 // lub: const visitorsArray = [...arguments]; // (ES6+)
5
6 console.log(`Zalogowano ${visitorsArray.length} gości.`);
7
8 // Teraz możemy używać metod tablicowych
9 visitorsArray.forEach((visitor, index) => {
10 console.log(`Gość #${index + 1}: ${visitor}`);
11 });
12}
13
14logVisitors("Ian Malcolm", "Alan Grant", "Dennis Nedry");
15// Zalogowano 3 gości.
16// Gość #1: Ian Malcolm
17// Gość #2: Alan Grant
18// Gość #3: Dennis NedryNowszy i bardziej elegancki sposób obsługi zmiennej liczby argumentów to parametr rest, wprowadzony w ES6:
1function trackDinosaurs(...dinosaurs) {
2 console.log(`Śledzenie ${dinosaurs.length} dinozaurów:`);
3
4 dinosaurs.forEach((dino, index) => {
5 console.log(`${index + 1}. Lokalizowanie: ${dino}`);
6 });
7}
8
9trackDinosaurs("T-Rex", "Velociraptor", "Triceratops", "Brachiosaurus");
10// Śledzenie 4 dinozaurów:
11// 1. Lokalizowanie: T-Rex
12// 2. Lokalizowanie: Velociraptor
13// 3. Lokalizowanie: Triceratops
14// 4. Lokalizowanie: BrachiosaurusParametr rest zbiera wszystkie pozostałe argumenty do tablicy. Musi być ostatnim parametrem w funkcji, ale może być poprzedzony zwykłymi parametrami:
1function assignTasks(teamLead, date, ...teamMembers) {
2 console.log(`Zadania na ${date} dla zespołu prowadzonego przez ${teamLead}:`);
3
4 teamMembers.forEach(member => {
5 console.log(`- ${member}: ${generateRandomTask()}`);
6 });
7}
8
9function generateRandomTask() {
10 const tasks = [
11 "Sprawdzenie ogrodzeń",
12 "Karmienie dinozaurów",
13 "Zbieranie próbek DNA",
14 "Monitorowanie zdrowia zwierząt",
15 "Konserwacja systemów bezpieczeństwa"
16 ];
17 return tasks[Math.floor(Math.random() * tasks.length)];
18}
19
20assignTasks("Dr. Henry Wu", "2023-06-15", "John", "Sarah", "Michael");
21// Zadania na 2023-06-15 dla zespołu prowadzonego przez Dr. Henry Wu:
22// - John: Sprawdzenie ogrodzeń
23// - Sarah: Karmienie dinozaurów
24// - Michael: Monitorowanie zdrowia zwierzątOperator spread (
...) jest przeciwieństwem parametru rest. Zamiast zbierać argumenty w tablicę, rozkłada tablicę (lub inny iterowalny obiekt) na poszczególne argumenty:1function monitorVitalSigns(dinoName, temperature, heartRate, respirationRate) {
2 console.log(`Monitoring parametrów życiowych dla: ${dinoName}`);
3 console.log(`- Temperatura: ${temperature}°C`);
4 console.log(`- Tętno: ${heartRate} uderzeń/min`);
5 console.log(`- Częstość oddychania: ${respirationRate} oddechów/min`);
6
7 // Analiza parametrów i generowanie raportu
8 if (temperature > 39 || heartRate > 140 || respirationRate > 30) {
9 return "ALERT: Parametry życiowe poza normą!";
10 }
11 return "Parametry życiowe w normie.";
12}
13
14// Tablica z danymi o parametrach życiowych
15const rexVitals = ["T-Rex", 38.2, 115, 22];
16
17// Bez operatora spread
18monitorVitalSigns(rexVitals[0], rexVitals[1], rexVitals[2], rexVitals[3]);
19
20// Z operatorem spread - rozkłada tablicę na oddzielne argumenty
21monitorVitalSigns(...rexVitals);Operator spread jest również przydatny przy łączeniu tablic lub obiektów:
1// Łączenie danych o dinozaurach z różnych systemów
2const basicDinoData = {
3 id: "VLR-01",
4 name: "Blue",
5 species: "Velociraptor"
6};
7
8const healthData = {
9 weight: 100,
10 health: "Dobry",
11 lastCheckup: "2023-05-10"
12};
13
14const behavioralData = {
15 temperament: "Agresywny",
16 packStatus: "Beta",
17 trainingLevel: "Zaawansowany"
18};
19
20// Tworzenie kompletnego obiektu danych o dinozaurze
21const completeProfile = {
22 ...basicDinoData,
23 ...healthData,
24 ...behavioralData,
25 updatedAt: new Date().toISOString()
26};
27
28console.log(completeProfile);
29// {
30// id: "VLR-01",
31// name: "Blue",
32// species: "Velociraptor",
33// weight: 100,
34// health: "Dobry",
35// lastCheckup: "2023-05-10",
36// temperament: "Agresywny",
37// packStatus: "Beta",
38// trainingLevel: "Zaawansowany",
39// updatedAt: "2023-06-15T10:30:00.000Z"
40// }Gdy funkcje przyjmują wiele parametrów, często łatwiej jest przekazać je jako obiekt. Destrukturyzacja (ES6+) pozwala na eleganckie wyodrębnienie tych wartości:
1// Funkcja akceptująca obiekt opcji
2function createDinosaurEnclosure({ species, capacity, securityLevel, location, features = [] }) {
3 console.log(`Tworzenie zagrody dla gatunku: ${species}`);
4 console.log(`Pojemność: ${capacity} osobników`);
5 console.log(`Poziom zabezpieczeń: ${securityLevel}/5`);
6 console.log(`Lokalizacja: ${location}`);
7
8 if (features.length > 0) {
9 console.log("Dodatkowe funkcje zagrody:");
10 features.forEach(feature => console.log(`- ${feature}`));
11 }
12
13 return `Zagroda dla ${species} została utworzona w lokalizacji ${location}`;
14}
15
16// Wywołanie z obiektem opcji
17const enclosureResult = createDinosaurEnclosure({
18 species: "Triceratops",
19 capacity: 6,
20 securityLevel: 3,
21 location: "Sektor B",
22 features: ["Zbiornik wodny", "Roślinność z okresu kredy", "Platforma obserwacyjna"]
23});
24
25console.log(enclosureResult);
26// "Zagroda dla Triceratops została utworzona w lokalizacji Sektor B"Możemy również ustawić domyślne wartości dla całego obiektu:
1function scheduleLabTest({
2 specimenId,
3 testType = "DNA",
4 priority = "Normal",
5 assignedTo = "Dr. Wu"
6} = {}) { // Domyślny pusty obiekt zapobiega błędom, gdy nie podamy argumentu
7
8 console.log(`Zaplanowano test ${testType} dla próbki #${specimenId}`);
9 console.log(`Priorytet: ${priority}`);
10 console.log(`Przypisany do: ${assignedTo}`);
11
12 return `Test zaplanowany. ID testu: TEST-${specimenId}-${Date.now()}`;
13}
14
15// Wywołanie z wszystkimi opcjami
16scheduleLabTest({
17 specimenId: "VLR-42",
18 testType: "Sekwencjonowanie genomu",
19 priority: "Wysoki",
20 assignedTo: "Dr. Sattler"
21});
22
23// Wywołanie z tylko wymaganym parametrem
24scheduleLabTest({ specimenId: "TRX-05" });
25
26// Wywołanie bez argumentów (zadziała dzięki domyślnemu pustemu obiektowi)
27// scheduleLabTest(); // Bez domyślnego pustego obiektu to by wywołało błądPołączmy poznane koncepcje, tworząc kompleksowy system zarządzania incydentami dla Parku Jurajskiego:
1// System zarządzania incydentami
2const incidentManager = (function() {
3 // Prywatna tablica incydentów
4 const incidents = [];
5
6 // Prywatne funkcje pomocnicze
7 function generateIncidentId() {
8 return `INC-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
9 }
10
11 function notifyTeam(incidentType, location, ...personnel) {
12 console.log(`ALERT: Incydent typu "${incidentType}" w lokalizacji "${location}"`);
13
14 if (personnel.length > 0) {
15 console.log(`Powiadamianie personelu: ${personnel.join(', ')}`);
16 } else {
17 console.log("Powiadamianie całego personelu bezpieczeństwa");
18 }
19 }
20
21 // Publiczne API
22 return {
23 // Metoda do raportowania nowego incydentu
24 reportIncident: function({
25 type,
26 location,
27 description,
28 severity = "Średni",
29 reportedBy,
30 affectedAssets = [],
31 notifyPersonnel = []
32 }) {
33 const incidentId = generateIncidentId();
34
35 const incident = {
36 id: incidentId,
37 type,
38 location,
39 description,
40 severity,
41 reportedBy,
42 affectedAssets,
43 status: "Nowy",
44 reportedAt: new Date().toISOString(),
45 resolvedAt: null
46 };
47
48 incidents.push(incident);
49
50 // Powiadomienie personelu
51 notifyTeam(type, location, ...notifyPersonnel);
52
53 return {
54 incidentId,
55 message: `Incydent #${incidentId} został zarejestrowany`
56 };
57 },
58
59 // Metoda do aktualizacji statusu incydentu
60 updateIncidentStatus: function(incidentId, newStatus, resolvedBy = null) {
61 const incident = incidents.find(inc => inc.id === incidentId);
62
63 if (!incident) {
64 return { success: false, message: "Incydent nie znaleziony" };
65 }
66
67 incident.status = newStatus;
68
69 if (newStatus === "Rozwiązany") {
70 incident.resolvedAt = new Date().toISOString();
71 incident.resolvedBy = resolvedBy;
72 }
73
74 return {
75 success: true,
76 message: `Status incydentu #${incidentId} zaktualizowany na "${newStatus}"`
77 };
78 },
79
80 // Metoda do pobierania informacji o incydencie
81 getIncidentInfo: function(incidentId) {
82 const incident = incidents.find(inc => inc.id === incidentId);
83
84 if (!incident) {
85 return null;
86 }
87
88 return { ...incident }; // Zwracamy kopię, aby uniknąć modyfikacji oryginału
89 },
90
91 // Metoda do pobierania listy incydentów według kryteriów
92 getIncidents: function({ status, type, severity, limit = 10 } = {}) {
93 let result = [...incidents]; // Kopia tablicy
94
95 // Filtrowanie według kryteriów
96 if (status) {
97 result = result.filter(inc => inc.status === status);
98 }
99
100 if (type) {
101 result = result.filter(inc => inc.type === type);
102 }
103
104 if (severity) {
105 result = result.filter(inc => inc.severity === severity);
106 }
107
108 // Sortowanie od najnowszych
109 result.sort((a, b) => new Date(b.reportedAt) - new Date(a.reportedAt));
110
111 // Limit wyników
112 return result.slice(0, limit);
113 }
114 };
115})();
116
117// Przykładowe użycie systemu
118const breakoutIncident = incidentManager.reportIncident({
119 type: "Ucieczka dinozaura",
120 location: "Zagroda Velociraptorów - Sektor C",
121 description: "Trzy velociraptory wydostały się z zagrody. Personel parku ewakuowany.",
122 severity: "Krytyczny",
123 reportedBy: "Robert Muldoon",
124 affectedAssets: ["VLR-02", "VLR-03", "VLR-04"],
125 notifyPersonnel: ["Dr. Grant", "John Hammond", "Ellie Sattler", "Ian Malcolm"]
126});
127
128console.log(breakoutIncident.message);
129// "Incydent #INC-1623844529123-456 został zarejestrowany"
130
131// Aktualizacja statusu incydentu
132const updateResult = incidentManager.updateIncidentStatus(
133 breakoutIncident.incidentId,
134 "W trakcie",
135 "Zespół SWAT"
136);
137console.log(updateResult.message);
138// "Status incydentu #INC-1623844529123-456 zaktualizowany na "W trakcie""
139
140// Pobranie informacji o incydencie
141const incidentDetails = incidentManager.getIncidentInfo(breakoutIncident.incidentId);
142console.log(incidentDetails);
143
144// Wyszukiwanie incydentów według kryteriów
145const criticalIncidents = incidentManager.getIncidents({
146 severity: "Krytyczny",
147 limit: 5
148});
149console.log(`Znaleziono ${criticalIncidents.length} krytycznych incydentów`);Ten przykład demonstruje:
Funkcje w JavaScript oferują wiele potężnych mechanizmów pracy z parametrami i argumentami, które pozwalają tworzyć elastyczne, czytelne i łatwe w użyciu API:
Dzięki tym mechanizmom możemy tworzyć funkcje, które są elastyczne, ale jednocześnie proste w użyciu - co jest kluczowe w zarządzaniu skomplikowanymi systemami, takimi jak nasz wirtualny Park Jurajski.
W następnej sekcji skupimy się na funkcjach strzałkowych, które wprowadzają bardziej zwięzłą składnię i specjalne zachowanie dotyczące kontekstu
this.