W systemie zarządzania Parkiem Jurajskim, właściwości i metody obiektów są kluczowe dla modelowania i kontrolowania zachowania różnych elementów naszego kompleksu. W tym module zagłębimy się bardziej szczegółowo w właściwości i metody obiektów JavaScript, poznając zaawansowane techniki, które pomagają nam utrzymać nasz park i jego mieszkańców w idealnym stanie.
W JavaScript każda właściwość obiektu ma nie tylko wartość, ale również atrybuty kontrolujące, jak ta właściwość może być używana. Te atrybuty to tzw. deskryptory właściwości.
1const dinosaur = {
2 name: "Blue"
3};
4
5// Pobieranie deskryptora właściwości
6const descriptor = Object.getOwnPropertyDescriptor(dinosaur, "name");
7console.log(descriptor);
8// Wynik: { value: "Blue", writable: true, enumerable: true, configurable: true }Deskryptor właściwości zawiera następujące pola:
Możemy zdefiniować nowe właściwości z określonymi deskryptorami za pomocą
Object.defineProperty():1const velociraptor = {};
2
3// Definiujemy właściwość "species" z określonymi deskryptorami
4Object.defineProperty(velociraptor, "species", {
5 value: "Velociraptor",
6 writable: false, // Nie możemy zmienić wartości
7 enumerable: true, // Pojawi się podczas iteracji
8 configurable: false // Nie możemy zmienić deskryptora ani usunąć właściwości
9});
10
11console.log(velociraptor.species); // "Velociraptor"
12
13// Próba zmiany nie zadziała (w trybie ścisłym rzuci błąd)
14velociraptor.species = "Tyrannosaurus";
15console.log(velociraptor.species); // Nadal "Velociraptor"
16
17// Próba usunięcia nie zadziała
18delete velociraptor.species; // Zwróci false
19console.log(velociraptor.species); // Nadal "Velociraptor"Możemy również zdefiniować wiele właściwości naraz:
1const triceratops = {};
2
3Object.defineProperties(triceratops, {
4 species: {
5 value: "Triceratops",
6 writable: false
7 },
8 diet: {
9 value: "herbivore",
10 writable: false
11 },
12 age: {
13 value: 5,
14 writable: true // Wiek może się zmieniać
15 }
16});
17
18console.log(triceratops.species); // "Triceratops"
19console.log(triceratops.diet); // "herbivore"
20console.log(triceratops.age); // 5
21
22triceratops.age = 6;
23console.log(triceratops.age); // 6Gettery i settery to specjalne metody, które pozwalają na kontrolę dostępu do właściwości obiektu.
1const dinosaur = {
2 _health: 100, // Konwencja podkreślenia wskazuje, że to prywatna właściwość
3
4 // Getter - wykonuje się, gdy próbujemy odczytać właściwość health
5 get health() {
6 console.log("Odczytywanie poziomu zdrowia");
7 return this._health;
8 },
9
10 // Setter - wykonuje się, gdy próbujemy przypisać wartość do właściwości health
11 set health(value) {
12 if (value < 0) {
13 console.log("Ostrzeżenie: Próba ustawienia zdrowia poniżej 0");
14 this._health = 0;
15 } else if (value > 100) {
16 console.log("Ostrzeżenie: Próba ustawienia zdrowia powyżej 100");
17 this._health = 100;
18 } else {
19 console.log(`Ustawianie poziomu zdrowia na ${value}`);
20 this._health = value;
21 }
22 }
23};
24
25console.log(dinosaur.health); // Uruchamia getter, wynik: 100
26dinosaur.health = 80; // Uruchamia setter
27console.log(dinosaur.health); // 80
28dinosaur.health = 150; // "Ostrzeżenie: Próba ustawienia zdrowia powyżej 100"
29console.log(dinosaur.health); // 100
30dinosaur.health = -20; // "Ostrzeżenie: Próba ustawienia zdrowia poniżej 0"
31console.log(dinosaur.health); // 0Gettery i settery możemy również zdefiniować przy użyciu
Object.defineProperty():1const dinosaur = {
2 _name: "Rex",
3 _species: "Tyrannosaurus"
4};
5
6Object.defineProperty(dinosaur, "fullName", {
7 get: function() {
8 return `${this._name} the ${this._species}`;
9 },
10
11 set: function(value) {
12 // Zakładamy, że value ma format "Nazwa the Gatunek"
13 const parts = value.split(" the ");
14 if (parts.length === 2) {
15 this._name = parts[0];
16 this._species = parts[1];
17 } else {
18 console.log("Nieprawidłowy format. Użyj: 'Nazwa the Gatunek'");
19 }
20 },
21
22 enumerable: true,
23 configurable: true
24});
25
26console.log(dinosaur.fullName); // "Rex the Tyrannosaurus"
27dinosaur.fullName = "Blue the Velociraptor";
28console.log(dinosaur._name); // "Blue"
29console.log(dinosaur._species); // "Velociraptor"Słowo kluczowe
this w metodach odnosi się do obiektu, na którym metoda jest wywoływana. Jednak w niektórych przypadkach (np. gdy przekazujemy metodę jako callback) możemy stracić kontekst this. Metoda bind pozwala na jawne ustawienie wartości this.1const rex = {
2 name: "Rex",
3 makeSound: function() {
4 console.log(`${this.name} ryczy głośno!`);
5 }
6};
7
8// Działanie normalne
9rex.makeSound(); // "Rex ryczy głośno!"
10
11// Problematyczne użycie - utrata kontekstu this
12const rexSound = rex.makeSound;
13// rexSound(); // Błąd: this.name jest undefined
14
15// Rozwiązanie - użycie bind, aby ustawić wartość this
16const boundRexSound = rex.makeSound.bind(rex);
17boundRexSound(); // "Rex ryczy głośno!"
18
19// Możemy również ustawić this na inny obiekt
20const blue = { name: "Blue" };
21const blueSound = rex.makeSound.bind(blue);
22blueSound(); // "Blue ryczy głośno!"Metody
call i apply pozwalają na wywołanie funkcji z określoną wartością this i argumentami:1function attack(target, damage) {
2 console.log(`${this.name} atakuje ${target} i zadaje ${damage} obrażeń!`);
3}
4
5const trex = { name: "Tyrannosaurus Rex" };
6const raptor = { name: "Velociraptor" };
7
8// Używamy call z jawnie przekazywanymi argumentami
9attack.call(trex, "Triceratops", 50);
10// "Tyrannosaurus Rex atakuje Triceratops i zadaje 50 obrażeń!"
11
12// Używamy apply z argumentami w tablicy
13attack.apply(raptor, ["Gallimimus", 30]);
14// "Velociraptor atakuje Gallimimus i zadaje 30 obrażeń!"Symbole zapewniają unikalne identyfikatory, które mogą być używane jako klucze właściwości obiektów. Są one szczególnie przydatne do tworzenia "prywatnych" właściwości lub do rozszerzania obiektów bez ryzyka kolizji nazw.
1// Tworzenie symboli
2const healthSymbol = Symbol('health');
3const nameSymbol = Symbol('name');
4
5const dinosaur = {
6 [nameSymbol]: "Blue",
7 [healthSymbol]: 100,
8 species: "Velociraptor"
9};
10
11console.log(dinosaur[nameSymbol]); // "Blue"
12console.log(dinosaur[healthSymbol]); // 100
13
14// Symbole nie są widoczne w pętlach for...in
15for (let key in dinosaur) {
16 console.log(key); // Tylko "species", bez nameSymbol i healthSymbol
17}
18
19// Ani w Object.keys()
20console.log(Object.keys(dinosaur)); // ["species"]
21
22// Ale można je uzyskać za pomocą Object.getOwnPropertySymbols()
23console.log(Object.getOwnPropertySymbols(dinosaur)); // [Symbol(name), Symbol(health)]JavaScript definiuje kilka "dobrze znanych symboli", które pozwalają klasom na dostosowanie swojego zachowania. Najczęściej używanym jest
Symbol.iterator, który umożliwia obiektom bycie iterowalnymi.1// Tworzymy obiekt DinosaurPack, który będzie iterowalny
2class DinosaurPack {
3 constructor() {
4 this.dinosaurs = [];
5 }
6
7 addDinosaur(name, species) {
8 this.dinosaurs.push({ name, species });
9 }
10
11 // Implementacja iterowalności
12 [Symbol.iterator]() {
13 let index = 0;
14 const dinosaurs = this.dinosaurs;
15
16 return {
17 next: function() {
18 if (index < dinosaurs.length) {
19 return { value: dinosaurs[index++], done: false };
20 } else {
21 return { done: true };
22 }
23 }
24 };
25 }
26}
27
28const pack = new DinosaurPack();
29pack.addDinosaur("Blue", "Velociraptor");
30pack.addDinosaur("Delta", "Velociraptor");
31pack.addDinosaur("Echo", "Velociraptor");
32pack.addDinosaur("Charlie", "Velociraptor");
33
34// Teraz możemy iterować po naszym obiekcie
35for (const dino of pack) {
36 console.log(`${dino.name} to ${dino.species}`);
37}
38// Wynik:
39// "Blue to Velociraptor"
40// "Delta to Velociraptor"
41// "Echo to Velociraptor"
42// "Charlie to Velociraptor"JavaScript oferuje trzy metody do kontrolowania modyfikowalności obiektów:
Kompletnie zamraża obiekt - nie można zmieniać istniejących właściwości ani dodawać nowych.
1const securityProtocol = {
2 level: "Maximum",
3 status: "Active",
4 lastUpdated: "2023-01-15"
5};
6
7// Zamrażamy obiekt
8Object.freeze(securityProtocol);
9
10// Próby modyfikacji nie zadziałają
11securityProtocol.level = "Minimum"; // Nie zmieni się
12securityProtocol.newProperty = "Value"; // Nie zostanie dodane
13
14console.log(securityProtocol.level); // Nadal "Maximum"
15console.log(securityProtocol.newProperty); // undefined
16
17console.log(Object.isFrozen(securityProtocol)); // trueUszczelnia obiekt - można modyfikować istniejące właściwości, ale nie można dodawać nowych ani usuwać istniejących.
1const dinoStats = {
2 health: 100,
3 hunger: 50
4};
5
6// Uszczelniamy obiekt
7Object.seal(dinoStats);
8
9// Możemy modyfikować istniejące właściwości
10dinoStats.health = 80;
11console.log(dinoStats.health); // 80
12
13// Ale nie możemy dodawać nowych
14dinoStats.happiness = 90; // Nie zadziała
15console.log(dinoStats.happiness); // undefined
16
17// Ani usuwać istniejących
18delete dinoStats.hunger; // Nie zadziała
19console.log(dinoStats.hunger); // Nadal 50
20
21console.log(Object.isSealed(dinoStats)); // trueZapobiega rozszerzaniu obiektu - nie można dodawać nowych właściwości, ale można modyfikować i usuwać istniejące.
1const dinosaur = {
2 name: "Blue",
3 species: "Velociraptor"
4};
5
6// Zapobiegamy rozszerzaniu
7Object.preventExtensions(dinosaur);
8
9// Nie możemy dodawać nowych właściwości
10dinosaur.age = 4; // Nie zadziała
11console.log(dinosaur.age); // undefined
12
13// Ale możemy modyfikować istniejące
14dinosaur.name = "Delta";
15console.log(dinosaur.name); // "Delta"
16
17// I usuwać istniejące
18delete dinosaur.species;
19console.log(dinosaur.species); // undefined
20
21console.log(Object.isExtensible(dinosaur)); // falseMetody proxy pozwalają na przechwytywanie i dostosowywanie podstawowych operacji na obiektach.
1const dinosaur = {
2 name: "Blue",
3 species: "Velociraptor",
4 health: 100,
5 hunger: 50,
6 aggressionLevel: 8
7};
8
9// Tworzymy proxy dla obiektu dinosaur
10const monitoredDinosaur = new Proxy(dinosaur, {
11 // Przechwytujemy operację pobierania właściwości
12 get: function(target, prop, receiver) {
13 console.log(`Odczytywanie właściwości: ${prop}`);
14 return Reflect.get(target, prop, receiver);
15 },
16
17 // Przechwytujemy operację ustawiania właściwości
18 set: function(target, prop, value, receiver) {
19 console.log(`Ustawianie ${prop} na ${value}`);
20
21 // Dodajemy walidację dla niektórych właściwości
22 if (prop === 'health') {
23 if (value < 0) value = 0;
24 if (value > 100) value = 100;
25
26 // Logowanie zmiany zdrowia
27 const oldValue = target[prop];
28 const change = value - oldValue;
29 console.log(`Zmiana zdrowia: ${change > 0 ? '+' : ''}${change}%`);
30
31 // Sprawdzanie czy dinozaur nie jest poważnie ranny
32 if (value < 20 && oldValue >= 20) {
33 console.log("ALARM: Dinozaur jest poważnie ranny!");
34 }
35 }
36
37 if (prop === 'aggressionLevel') {
38 if (value > 7) {
39 console.log("UWAGA: Wysoki poziom agresji - należy zwiększyć środki bezpieczeństwa!");
40 }
41 }
42
43 return Reflect.set(target, prop, value, receiver);
44 },
45
46 // Przechwytujemy operację usuwania właściwości
47 deleteProperty: function(target, prop) {
48 console.log(`Próba usunięcia ${prop} - operacja zabroniona dla bezpieczeństwa danych`);
49 return false; // Nie pozwalamy na usuwanie właściwości
50 }
51});
52
53// Testujemy nasze proxy
54console.log(monitoredDinosaur.name); // "Odczytywanie właściwości: name", "Blue"
55monitoredDinosaur.health = 90; // "Ustawianie health na 90", "Zmiana zdrowia: -10%"
56monitoredDinosaur.health = 15; // "Ustawianie health na 15", "Zmiana zdrowia: -75%", "ALARM: Dinozaur jest poważnie ranny!"
57monitoredDinosaur.aggressionLevel = 9; // "Ustawianie aggressionLevel na 9", "UWAGA: Wysoki poziom agresji..."
58delete monitoredDinosaur.hunger; // "Próba usunięcia hunger - operacja zabroniona dla bezpieczeństwa danych"Połączmy kilka poznanych technik, aby stworzyć zaawansowany system monitorowania zdrowia dinozaurów w naszym parku:
1class DinosaurHealthMonitor {
2 constructor() {
3 this.dinosaurs = new Map();
4 this.alarmThreshold = 30;
5 this.criticalThreshold = 15;
6 this.lastCheckTime = null;
7 }
8
9 // Dodajemy dinozaura do systemu monitorowania
10 addDinosaur(id, name, species) {
11 if (this.dinosaurs.has(id)) {
12 console.log(`Dinozaur o ID ${id} już istnieje w systemie.`);
13 return false;
14 }
15
16 const dinosaurData = {
17 id,
18 name,
19 species,
20 _health: 100,
21 _lastFed: new Date(),
22 _status: "Healthy",
23
24 // Używamy gettera i settera dla zdrowia
25 get health() {
26 return this._health;
27 },
28
29 set health(value) {
30 const oldHealth = this._health;
31 this._health = Math.max(0, Math.min(100, value)); // Ograniczenie do zakresu 0-100
32
33 // Aktualizacja statusu na podstawie zdrowia
34 if (this._health === 0) {
35 this._status = "Critical - Emergency Response Required";
36 } else if (this._health < 15) {
37 this._status = "Critical";
38 } else if (this._health < 30) {
39 this._status = "Poor - Medical Attention Required";
40 } else if (this._health < 60) {
41 this._status = "Fair";
42 } else if (this._health < 90) {
43 this._status = "Good";
44 } else {
45 this._status = "Excellent";
46 }
47
48 return this._health;
49 },
50
51 // Metoda karmienia dinozaura
52 feed() {
53 const now = new Date();
54 const hoursSinceLastFed = (now - this._lastFed) / (1000 * 60 * 60);
55
56 // Efekt karmienia zależy od tego, jak dawno dinozaur był ostatnio karmiony
57 if (hoursSinceLastFed < 1) {
58 console.log(`${this.name} nie jest głodny (karmiony mniej niż godzinę temu).`);
59 return false;
60 } else {
61 const healthGain = Math.min(10, hoursSinceLastFed * 2); // Maksymalnie 10 punktów zdrowia
62 this.health += healthGain;
63 this._lastFed = now;
64
65 console.log(`${this.name} został nakarmiony. +${healthGain.toFixed(1)} do zdrowia. Aktualny poziom: ${this.health}%`);
66 return true;
67 }
68 }
69 };
70
71 // Używamy Object.seal, aby zapobiec dodawaniu nowych właściwości
72 Object.seal(dinosaurData);
73
74 // Dodajemy dinozaura do mapy
75 this.dinosaurs.set(id, dinosaurData);
76 console.log(`Dodano dinozaura ${name} (${species}) do systemu monitorowania zdrowia.`);
77 return true;
78 }
79
80 // Pobieranie informacji o dinozaurze
81 getDinosaurInfo(id) {
82 if (!this.dinosaurs.has(id)) {
83 return `Dinozaur o ID ${id} nie istnieje w systemie.`;
84 }
85
86 const dino = this.dinosaurs.get(id);
87 return {
88 id: dino.id,
89 name: dino.name,
90 species: dino.species,
91 health: dino.health,
92 status: dino._status,
93 lastFed: dino._lastFed
94 };
95 }
96
97 // Metoda do symulacji upływu czasu, która obniża zdrowie dinozaurów
98 simulateTimePass(hours) {
99 console.log(`Symulacja upływu ${hours} godzin...`);
100
101 this.dinosaurs.forEach(dino => {
102 // Obniżenie zdrowia - tempo zależy od gatunku
103 let healthReduction = hours;
104
105 if (dino.species.toLowerCase().includes('tyrannosaurus')) {
106 healthReduction *= 1.5; // Tyrannosaurus traci zdrowie szybciej
107 } else if (dino.species.toLowerCase().includes('velociraptor')) {
108 healthReduction *= 1.2; // Velociraptory też dość szybko
109 }
110
111 const newHealth = dino.health - healthReduction;
112 dino.health = newHealth;
113
114 console.log(`${dino.name}: Zdrowie zmniejszone o ${healthReduction.toFixed(1)} do ${dino.health}% (${dino._status})`);
115 });
116
117 this.lastCheckTime = new Date();
118 this.checkAlarms();
119 }
120
121 // Sprawdzanie alarmów zdrowotnych
122 checkAlarms() {
123 console.log("Sprawdzanie alarmów zdrowotnych...");
124
125 const criticalDinos = [];
126 const alarmDinos = [];
127
128 this.dinosaurs.forEach(dino => {
129 if (dino.health <= this.criticalThreshold) {
130 criticalDinos.push(dino);
131 } else if (dino.health <= this.alarmThreshold) {
132 alarmDinos.push(dino);
133 }
134 });
135
136 if (criticalDinos.length > 0) {
137 console.log("=== KRYTYCZNY STAN ZDROWIA ===");
138 criticalDinos.forEach(dino => {
139 console.log(`- ${dino.name} (${dino.species}): ${dino.health}% - WYMAGA NATYCHMIASTOWEJ INTERWENCJI!`);
140 });
141 }
142
143 if (alarmDinos.length > 0) {
144 console.log("=== ALARM ZDROWOTNY ===");
145 alarmDinos.forEach(dino => {
146 console.log(`- ${dino.name} (${dino.species}): ${dino.health}% - Wymaga opieki weterynaryjnej`);
147 });
148 }
149
150 if (criticalDinos.length === 0 && alarmDinos.length === 0) {
151 console.log("Wszystkie dinozaury są w dobrej kondycji zdrowotnej.");
152 }
153 }
154
155 // Generowanie raportu dla wszystkich dinozaurów
156 generateHealthReport() {
157 const now = new Date();
158 let report = `=== RAPORT ZDROWIA DINOZAURÓW (wygenerowany: ${now.toLocaleString()}) ===
159`;
160
161 if (this.dinosaurs.size === 0) {
162 report += "Brak dinozaurów w systemie monitorowania.
163";
164 } else {
165 // Sortowanie dinozaurów według poziomu zdrowia (od najniższego)
166 const sortedDinos = [...this.dinosaurs.values()].sort((a, b) => a.health - b.health);
167
168 sortedDinos.forEach(dino => {
169 report += `${dino.name} (${dino.species})
170`;
171 report += ` Zdrowie: ${dino.health}% (${dino._status})
172`;
173 report += ` Ostatnio karmiony: ${dino._lastFed.toLocaleString()}
174`;
175
176 // Dodajemy rekomendacje
177 if (dino.health < this.criticalThreshold) {
178 report += " REKOMENDACJA: Natychmiastowa interwencja weterynaryjna! Przeniesienie do ośrodka intensywnej opieki.
179";
180 } else if (dino.health < this.alarmThreshold) {
181 report += " REKOMENDACJA: Zwiększona częstotliwość karmienia i kontroli weterynaryjnej.
182";
183 }
184
185 report += "
186";
187 });
188 }
189
190 return report;
191 }
192}
193
194// Przyklad użycia
195const healthMonitor = new DinosaurHealthMonitor();
196
197// Dodajemy kilka dinozaurów
198healthMonitor.addDinosaur("TRX-01", "Rex", "Tyrannosaurus Rex");
199healthMonitor.addDinosaur("VEL-01", "Blue", "Velociraptor");
200healthMonitor.addDinosaur("TRI-01", "Trixie", "Triceratops");
201healthMonitor.addDinosaur("BRACH-01", "Bronty", "Brachiosaurus");
202
203// Generujemy raport początkowy
204console.log(healthMonitor.generateHealthReport());
205
206// Symulujemy upływ czasu
207healthMonitor.simulateTimePass(24); // 24 godziny
208
209// Karmimy niektóre dinozaury
210const rex = healthMonitor.dinosaurs.get("TRX-01");
211const blue = healthMonitor.dinosaurs.get("VEL-01");
212rex.feed();
213blue.feed();
214
215// Symulujemy kolejny upływ czasu
216healthMonitor.simulateTimePass(48); // 48 godzin
217
218// Generujemy końcowy raport
219console.log(healthMonitor.generateHealthReport());W tym module poznaliśmy zaawansowane właściwości i metody obiektów JavaScript:
Te techniki są niezbędne przy tworzeniu złożonych systemów, takich jak nasz system monitorowania zdrowia dinozaurów w Parku Jurajskim. Pozwalają na tworzenie bardziej niezawodnych, bezpiecznych i eleganckich rozwiązań programistycznych.