Kurs JavaScript i TypeScript · Moduł 2: Funkcje, tablice i obiekty
Właściwości i metody obiektów
W tej lekcji9
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 tej lekcji przyjrzymy się bliżej właściwościom i metodom obiektów JavaScript, poznając zaawansowane techniki, które pomagają nam utrzymać nasz park i jego mieszkańców w idealnym stanie.
Deskryptory właściwości
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:
- value: aktualna wartość właściwości
- writable: czy właściwość może być zmieniona
- enumerable: czy właściwość pojawia się podczas iteracji (np. w pętli for...in)
- configurable: czy deskryptor może być zmieniony i czy właściwość może być usunięta
Definiowanie właściwości z deskryptorami
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
Gettery 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"Metody obiektów i ich zaawansowane właściwości
Wiązanie this - metoda bind
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
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ń!"Właściwości obiektów jako symbole
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)]Dobrze znane symbole
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"Zamrożenie, uszczelnienie i zapobieganie rozszerzaniu obiektów
JavaScript oferuje trzy metody do kontrolowania modyfikowalności obiektów:
Object.freeze()
Zamraża obiekt (płytko: obiekty zagnieżdżone nadal można zmieniać) - 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)); // trueObject.seal()
Uszczelnia 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)); // trueObject.preventExtensions()
Zapobiega 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)); // falseObiekt Proxy
Obiekt Proxy pozwala 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"Praktyczne zastosowanie: System monitorowania zdrowia dinozaurów
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()}) ===\n`;
159
160 if (this.dinosaurs.size === 0) {
161 report += "Brak dinozaurów w systemie monitorowania.\n";
162 } else {
163 // Sortowanie dinozaurów według poziomu zdrowia (od najniższego)
164 const sortedDinos = [...this.dinosaurs.values()].sort((a, b) => a.health - b.health);
165
166 sortedDinos.forEach(dino => {
167 report += `${dino.name} (${dino.species})\n`;
168 report += ` Zdrowie: ${dino.health}% (${dino._status})\n`;
169 report += ` Ostatnio karmiony: ${dino._lastFed.toLocaleString()}\n`;
170
171 // Dodajemy rekomendacje
172 if (dino.health < this.criticalThreshold) {
173 report += " REKOMENDACJA: Natychmiastowa interwencja weterynaryjna! Przeniesienie do ośrodka intensywnej opieki.\n";
174 } else if (dino.health < this.alarmThreshold) {
175 report += " REKOMENDACJA: Zwiększona częstotliwość karmienia i kontroli weterynaryjnej.\n";
176 }
177
178 report += "\n";
179 });
180 }
181
182 return report;
183 }
184}
185
186// Przykład użycia
187const healthMonitor = new DinosaurHealthMonitor();
188
189// Dodajemy kilka dinozaurów
190healthMonitor.addDinosaur("TRX-01", "Rex", "Tyrannosaurus Rex");
191healthMonitor.addDinosaur("VEL-01", "Blue", "Velociraptor");
192healthMonitor.addDinosaur("TRI-01", "Trixie", "Triceratops");
193healthMonitor.addDinosaur("BRACH-01", "Bronty", "Brachiosaurus");
194
195// Generujemy raport początkowy
196console.log(healthMonitor.generateHealthReport());
197
198// Symulujemy upływ czasu
199healthMonitor.simulateTimePass(24); // 24 godziny
200
201// Karmimy niektóre dinozaury
202const rex = healthMonitor.dinosaurs.get("TRX-01");
203const blue = healthMonitor.dinosaurs.get("VEL-01");
204rex.feed();
205blue.feed();
206
207// Symulujemy kolejny upływ czasu
208healthMonitor.simulateTimePass(48); // 48 godzin
209
210// Generujemy końcowy raport
211console.log(healthMonitor.generateHealthReport());Podsumowanie
W tej lekcji poznaliśmy zaawansowane właściwości i metody obiektów JavaScript:
- Deskryptory właściwości - pozwalają na precyzyjną kontrolę nad atrybutami właściwości
- Gettery i settery - umożliwiają wykonanie kodu podczas odczytu lub zapisu właściwości
- Wiązanie this - metody bind, call i apply pomagają w kontrolowaniu kontekstu funkcji
- Symbole - zapewniają unikalne identyfikatory dla właściwości obiektów
- Zamrażanie obiektów - metody freeze, seal i preventExtensions pozwalają ograniczyć modyfikowalność obiektów
- Proxy - pozwala na przechwytywanie i dostosowywanie podstawowych operacji na obiektach
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.
Kod do tej lekcji: index.js
1// Właściwości i metody obiektów - Park Jurajski
2console.log("Właściwości i Metody Obiektów");
3console.log("Zaawansowane operacje na obiektach\n");
4
5// ===========================================
6// 1. DOSTĘP DO WŁAŚCIWOŚCI
7// ===========================================
8console.log("=== 1. Dostęp do właściwości ===");
9
10const dinosaur = {
11 name: "T-Rex",
12 species: "Tyrannosaurus Rex",
13 age: 8,
14 "sector-id": "A-7", // Właściwość z myślnikiem
15 weight: 8000
16};
17
18// Notacja kropkowa
19console.log("\nNotacja kropkowa:");
20console.log(` name: ${dinosaur.name}`);
21console.log(` age: ${dinosaur.age}`);
22
23// Notacja nawiasowa
24console.log("\nNotacja nawiasowa:");
25console.log(` ['species']: ${dinosaur['species']}`);
26console.log(` ['sector-id']: ${dinosaur['sector-id']}`); // Wymagane dla myślników
27
28// Dynamiczny dostęp
29const prop = "weight";
30console.log(`\nDynamiczny dostęp [${prop}]: ${dinosaur[prop]}`);
31
32// ===========================================
33// 2. DODAWANIE I MODYFIKOWANIE WŁAŚCIWOŚCI
34// ===========================================
35console.log("\n=== 2. Dodawanie i modyfikowanie ===");
36
37const dino = {
38 name: "Blue",
39 species: "Velociraptor"
40};
41
42console.log("\nPoczątkowo:", dino);
43
44// Dodawanie nowych właściwości
45dino.age = 5;
46dino.weight = 80;
47dino["sector"] = "B";
48
49console.log("Po dodaniu:", dino);
50
51// Modyfikowanie istniejących
52dino.age = 6;
53dino.weight = 85;
54
55console.log("Po modyfikacji:", dino);
56
57// ===========================================
58// 3. USUWANIE WŁAŚCIWOŚCI
59// ===========================================
60console.log("\n=== 3. Usuwanie właściwości ===");
61
62const tempDino = {
63 name: "Charlie",
64 species: "Velociraptor",
65 tempId: 12345,
66 debugMode: true
67};
68
69console.log("\nPrzed usunięciem:", tempDino);
70
71delete tempDino.tempId;
72delete tempDino.debugMode;
73
74console.log("Po usunięciu:", tempDino);
75
76// ===========================================
77// 4. SPRAWDZANIE ISTNIENIA WŁAŚCIWOŚCI
78// ===========================================
79console.log("\n=== 4. Sprawdzanie istnienia ===");
80
81const checkDino = {
82 name: "Rexy",
83 age: 8,
84 sector: null
85};
86
87// Operator 'in'
88console.log("\nOperator 'in':");
89console.log(` 'name' in checkDino: ${'name' in checkDino}`);
90console.log(` 'weight' in checkDino: ${'weight' in checkDino}`);
91console.log(` 'sector' in checkDino: ${'sector' in checkDino}`); // true mimo null!
92
93// hasOwnProperty()
94console.log("\nhasOwnProperty():");
95console.log(` hasOwnProperty('name'): ${checkDino.hasOwnProperty('name')}`);
96console.log(` hasOwnProperty('toString'): ${checkDino.hasOwnProperty('toString')}`); // false (odziedziczona)
97
98// undefined check
99console.log("\nSprawdzanie undefined:");
100console.log(` name !== undefined: ${checkDino.name !== undefined}`);
101console.log(` weight !== undefined: ${checkDino.weight !== undefined}`);
102
103// ===========================================
104// 5. ITERACJA PO WŁAŚCIWOŚCIACH
105// ===========================================
106console.log("\n=== 5. Iteracja po właściwościach ===");
107
108const iterDino = {
109 name: "Trike",
110 species: "Triceratops",
111 age: 7,
112 weight: 9000,
113 sector: "C"
114};
115
116// for...in
117console.log("\nfor...in:");
118for (const key in iterDino) {
119 console.log(` ${key}: ${iterDino[key]}`);
120}
121
122// Object.keys()
123console.log("\nObject.keys():");
124const keys = Object.keys(iterDino);
125console.log(` Klucze: ${keys.join(", ")}`);
126
127// Object.values()
128console.log("\nObject.values():");
129const values = Object.values(iterDino);
130console.log(` Wartości: ${values.join(", ")}`);
131
132// Object.entries()
133console.log("\nObject.entries():");
134Object.entries(iterDino).forEach(([key, value]) => {
135 console.log(` ${key} => ${value}`);
136});
137
138// ===========================================
139// 6. METODY OBIEKTÓW
140// ===========================================
141console.log("\n=== 6. Metody obiektów ===");
142
143const methodDino = {
144 name: "Stego",
145 species: "Stegosaurus",
146 health: 85,
147
148 // Metoda tradycyjna
149 getInfo: function() {
150 return `${this.name} (${this.species})`;
151 },
152
153 // Metoda skrócona (ES6)
154 roar() {
155 return `${this.name} says: ROAARRR!`;
156 },
157
158 // Metoda z parametrami
159 updateHealth(change) {
160 this.health += change;
161 if (this.health > 100) this.health = 100;
162 if (this.health < 0) this.health = 0;
163 return `Zdrowie: ${this.health}%`;
164 },
165
166 // Metoda zwracająca obiekt
167 getStats() {
168 return {
169 name: this.name,
170 health: this.health,
171 status: this.health >= 80 ? "Zdrowy" : "Wymaga uwagi"
172 };
173 }
174};
175
176console.log("\n", methodDino.getInfo());
177console.log(" ", methodDino.roar());
178console.log(" ", methodDino.updateHealth(10));
179console.log(" Stats:", methodDino.getStats());
180
181// ===========================================
182// 7. THIS W METODACH
183// ===========================================
184console.log("\n=== 7. Kontekst 'this' w metodach ===");
185
186const thisDino = {
187 name: "Brachio",
188 sector: "D",
189
190 showInfo() {
191 console.log(`\n${this.name} w sektorze ${this.sector}`);
192 },
193
194 // Problem z zagnieżdżoną funkcją
195 showWithTraditional() {
196 console.log(`\n${this.name}:`);
197
198 ["Karmienie", "Badanie", "Czyszczenie"].forEach(function(task) {
199 // this tutaj NIE odnosi się do thisDino!
200 console.log(` - ${task}`); // Nie możemy użyć this.name
201 });
202 },
203
204 // Rozwiązanie z arrow function
205 showWithArrow() {
206 console.log(`\n${this.name}:`);
207
208 ["Karmienie", "Badanie", "Czyszczenie"].forEach(task => {
209 console.log(` - ${task} dla ${this.name}`); // this działa!
210 });
211 }
212};
213
214thisDino.showInfo();
215thisDino.showWithTraditional();
216thisDino.showWithArrow();
217
218// ===========================================
219// 8. COMPUTED PROPERTY NAMES
220// ===========================================
221console.log("\n=== 8. Computed property names ===");
222
223const propName = "species";
224const propValue = "Velociraptor";
225const sectorNumber = 7;
226
227const computedDino = {
228 name: "Echo",
229 [propName]: propValue, // Obliczana nazwa właściwości
230 [`sector-${sectorNumber}`]: true,
231 [`health_${Date.now()}`]: 95
232};
233
234console.log("\nObiekt z obliczanymi nazwami:");
235console.log(computedDino);
236
237// ===========================================
238// 9. PROPERTY DESCRIPTORS
239// ===========================================
240console.log("\n=== 9. Property descriptors ===");
241
242const descriptorDino = {};
243
244// Definiowanie właściwości z deskryptorami
245Object.defineProperty(descriptorDino, 'name', {
246 value: "Rex",
247 writable: true,
248 enumerable: true,
249 configurable: true
250});
251
252Object.defineProperty(descriptorDino, 'id', {
253 value: 12345,
254 writable: false, // Tylko do odczytu
255 enumerable: false, // Nie pojawi się w for...in
256 configurable: false // Nie można usunąć ani zmienić deskryptora
257});
258
259console.log("\nWłaściwości:");
260console.log(` name: ${descriptorDino.name}`);
261console.log(` id: ${descriptorDino.id}`);
262
263console.log("\nPróba modyfikacji:");
264descriptorDino.name = "Rexy"; // Zadziała
265console.log(` name po zmianie: ${descriptorDino.name}`);
266
267descriptorDino.id = 99999; // Nie zadziała (writable: false)
268console.log(` id po próbie zmiany: ${descriptorDino.id}`);
269
270console.log("\nfor...in (id nie pojawi się):");
271for (const key in descriptorDino) {
272 console.log(` ${key}: ${descriptorDino[key]}`);
273}
274
275// ===========================================
276// 10. PRZYKŁAD KOMPLEKSOWY
277// ===========================================
278console.log("\n=== 10. System zarządzania dinozaurem - przykład kompleksowy ===");
279
280class DinosaurManager {
281 constructor(name, species) {
282 // Właściwości podstawowe
283 this._name = name;
284 this._species = species;
285 this._health = 100;
286 this._fed = false;
287 this._lastFed = null;
288 this._activities = [];
289
290 // Właściwości obliczane
291 Object.defineProperty(this, 'id', {
292 value: `DINO-${Date.now()}`,
293 writable: false,
294 enumerable: true
295 });
296 }
297
298 // Gettery i settery
299 get name() {
300 return this._name;
301 }
302
303 get species() {
304 return this._species;
305 }
306
307 get health() {
308 return this._health;
309 }
310
311 set health(value) {
312 if (value < 0) value = 0;
313 if (value > 100) value = 100;
314
315 const oldHealth = this._health;
316 this._health = value;
317
318 this.logActivity(`Zdrowie zmienione z ${oldHealth}% na ${value}%`);
319 }
320
321 get status() {
322 if (this._health >= 80) return "Doskonały";
323 if (this._health >= 50) return "Dobry";
324 if (this._health >= 20) return "Słaby";
325 return "Krytyczny";
326 }
327
328 get needsFeeding() {
329 if (!this._lastFed) return true;
330 const hoursSinceLastFed = (Date.now() - this._lastFed) / (1000 * 60 * 60);
331 return hoursSinceLastFed > 8;
332 }
333
334 // Metody akcji
335 feed() {
336 this._fed = true;
337 this._lastFed = Date.now();
338 this.health += 5;
339 this.logActivity("Nakarmiony");
340 return `${this._name} został nakarmiony`;
341 }
342
343 heal(amount) {
344 this.health += amount;
345 this.logActivity(`Leczony (+${amount}% zdrowia)`);
346 return `${this._name} wyleczony o ${amount}%`;
347 }
348
349 exercise() {
350 this.health -= 5;
351 this.logActivity("Ćwiczenia");
352 return `${this._name} wykonał ćwiczenia`;
353 }
354
355 // Metody pomocnicze
356 logActivity(activity) {
357 this._activities.push({
358 activity,
359 timestamp: new Date().toISOString(),
360 health: this._health
361 });
362 }
363
364 getActivityLog(limit = 5) {
365 return this._activities.slice(-limit);
366 }
367
368 getInfo() {
369 return {
370 id: this.id,
371 name: this._name,
372 species: this._species,
373 health: this._health,
374 status: this.status,
375 fed: this._fed,
376 needsFeeding: this.needsFeeding,
377 activitiesCount: this._activities.length
378 };
379 }
380
381 displayReport() {
382 console.log(`\n==================================================`);
383 console.log(`RAPORT DINOZAURA: ${this._name}`);
384 console.log("=".repeat(50));
385
386 const info = this.getInfo();
387 console.log(`ID: ${info.id}`);
388 console.log(`Gatunek: ${info.species}`);
389 console.log(`Zdrowie: ${info.health}% (${info.status})`);
390 console.log(`Nakarmiony: ${info.fed ? "Tak" : "Nie"}`);
391 console.log(`Wymaga karmienia: ${info.needsFeeding ? "Tak" : "Nie"}`);
392 console.log(`Liczba aktywności: ${info.activitiesCount}`);
393
394 const recentActivities = this.getActivityLog(5);
395 if (recentActivities.length > 0) {
396 console.log(`\nOstatnie aktywności:`);
397 recentActivities.forEach((act, i) => {
398 const time = new Date(act.timestamp).toLocaleTimeString();
399 console.log(` ${i + 1}. [${time}] ${act.activity} (zdrowie: ${act.health}%)`);
400 });
401 }
402 }
403
404 // Metoda statyczna
405 static compare(dino1, dino2) {
406 console.log(`\nPorównanie: ${dino1.name} vs ${dino2.name}`);
407 console.log(` Zdrowie: ${dino1.health}% vs ${dino2.health}%`);
408 console.log(` Status: ${dino1.status} vs ${dino2.status}`);
409
410 if (dino1.health > dino2.health) {
411 console.log(` ${dino1.name} jest zdrowszy`);
412 } else if (dino2.health > dino1.health) {
413 console.log(` ${dino2.name} jest zdrowszy`);
414 } else {
415 console.log(` Obaj mają takie samo zdrowie`);
416 }
417 }
418}
419
420// Demonstracja systemu
421const rexy = new DinosaurManager("Rexy", "T-Rex");
422const blue = new DinosaurManager("Blue", "Velociraptor");
423
424console.log();
425console.log(rexy.feed());
426console.log(rexy.exercise());
427console.log(rexy.heal(10));
428
429console.log();
430console.log(blue.feed());
431console.log(blue.exercise());
432console.log(blue.exercise());
433
434rexy.displayReport();
435blue.displayReport();
436
437DinosaurManager.compare(rexy, blue);
438
439console.log("\nGratulacje! Opanowałeś właściwości i metody obiektów!");Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Do czego służy Object.defineProperty() w JavaScript?
2. Do czego służą gettery (get) i settery (set) w obiektach JavaScript?
Zadania praktyczne w grze
- Klikanie w kolejności
Ułóż elementy definicji gettera w obiekcie: