Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Parametry i argumenty funkcji w JavaScript

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.

Różnica między parametrami a argumentami

Choć terminy "parametry" i "argumenty" są często używane zamiennie, istnieje między nimi subtelna różnica:

  • Parametry to zmienne zdefiniowane w deklaracji funkcji
  • Argumenty to rzeczywiste wartości przekazywane do funkcji podczas jej wywołania
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");

Parametry podstawowe

JavaScript oferuje kilka sposobów pracy z parametrami funkcji:

1. Parametry obowiązkowe i opcjonalne

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!

2. Parametry domyślne (ES6+)

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ów

Obsługa zmiennej liczby argumentów

W systemach tak złożonych jak Park Jurajski, czasem potrzebujemy funkcji, które mogą przyjmować zmienną liczbę argumentów.

1. Obiekt arguments

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 Sattler

Obiekt

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 Nedry

2. Parametr rest (ES6+)

Nowszy 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: Brachiosaurus

Parametr 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ąt

Operator rozproszenia (spread)

Operator 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// }

Destrukturyzacja parametrów obiektowych

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łąd

Praktyczny przykład: System zarządzania incydentami

Połą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:

  1. Użycie destrukturyzacji obiektów do przyjmowania parametrów funkcji
  2. Zastosowanie parametrów domyślnych
  3. Użycie parametru rest do obsługi zmiennej liczby argumentów
  4. Użycie operatora spread do przekazywania argumentów
  5. Zaawansowane przetwarzanie i filtrowanie danych na podstawie parametrów

Podsumowanie

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:

  • Parametry domyślne upraszczają obsługę opcjonalnych argumentów
  • Parametr rest umożliwia funkcjom przyjmowanie zmiennej liczby argumentów
  • Operator spread pozwala na przekazywanie elementów tablicy jako oddzielnych argumentów
  • Destrukturyzacja obiektów ułatwia pracę z wieloma parametrami poprzez ich organizację w obiekty

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
.

Przejdź do CodeWorlds