W Parku Jurajskim używamy obiektów JavaScript do reprezentowania wielu elementów naszego ekosystemu - od dinozaurów i ich zachowań, przez obszary parku, po dane pracowników i procedury bezpieczeństwa. Obiekty są fundamentalnym typem danych w JavaScript, umożliwiającym tworzenie złożonych struktur danych z właściwościami i metodami.
Najprostszym sposobem tworzenia obiektów w JavaScript jest użycie literału obiektu - notacji z nawiasami klamrowymi
{}.1// Prosty obiekt dinozaura utworzony za pomocą literału obiektu
2const velociraptor = {
3 name: "Blue",
4 species: "Velociraptor",
5 age: 4,
6 length: 2.1, // metry
7 weight: 80, // kilogramy
8 maxSpeed: 64, // km/h
9 diet: "carnivore",
10 location: "Sektor B",
11 dangerous: true
12};
13
14console.log(velociraptor.name); // "Blue"
15console.log(velociraptor.species); // "Velociraptor"
16console.log(velociraptor.maxSpeed); // 64Możemy uzyskać dostęp do właściwości obiektu na dwa sposoby:
1// Notacja kropkowa
2console.log(velociraptor.diet); // "carnivore"
3
4// Notacja nawiasowa (przydatna, gdy nazwa właściwości jest przechowywana w zmiennej)
5const propertyName = "location";
6console.log(velociraptor[propertyName]); // "Sektor B"
7
8// Notacja nawiasowa jest też przydatna, gdy nazwa właściwości zawiera znaki specjalne lub spacje
9const dinoDetails = {
10 "full name": "Velociraptor antirrhopus",
11 "security-level": "Alpha"
12};
13
14console.log(dinoDetails["full name"]); // "Velociraptor antirrhopus"
15console.log(dinoDetails["security-level"]); // "Alpha"Obiekty mogą zawierać również funkcje, które nazywamy metodami:
1const velociraptor = {
2 name: "Blue",
3 species: "Velociraptor",
4 age: 4,
5 hunger: 50, // procent (0-100)
6 health: 100, // procent (0-100)
7
8 // Metoda do karmienia dinozaura
9 feed: function() {
10 if (this.hunger <= 0) {
11 return "Blue nie jest głodna.";
12 }
13
14 this.hunger -= 30;
15 if (this.hunger < 0) this.hunger = 0;
16
17 return "Blue została nakarmiona. Poziom głodu: " + this.hunger + "%";
18 },
19
20 // Krótsza składnia metody (wprowadzona w ES6)
21 makeSound() {
22 return "Blue wydaje ostry, przenikliwy dźwięk!";
23 },
24
25 // Metoda sprawdzająca stan zdrowia
26 checkStatus() {
27 return `Dinozaur: ${this.name} (Gatunek: ${this.species})
28Status zdrowia: ${this.health}%
29Poziom głodu: ${this.hunger}%
30Stan ogólny: ${this.health > 80 ? "Doskonały" : this.health > 50 ? "Dobry" : "Wymaga opieki weterynaryjnej"}`;
31 }
32};
33
34console.log(velociraptor.makeSound()); // "Blue wydaje ostry, przenikliwy dźwięk!"
35console.log(velociraptor.feed()); // "Blue została nakarmiona. Poziom głodu: 20%"
36console.log(velociraptor.checkStatus());Słowo kluczowe
this w metodach odnosi się do obiektu, na którym metoda jest wywoływana.Możemy dynamicznie dodawać i usuwać właściwości obiektów JavaScript:
1const triceratops = {
2 name: "Trixie",
3 species: "Triceratops",
4 age: 6
5};
6
7// Dodawanie nowych właściwości
8triceratops.health = 95;
9triceratops["location"] = "Sektor C";
10
11// Dodawanie metody
12triceratops.updateHealth = function(amount) {
13 this.health += amount;
14 if (this.health > 100) this.health = 100;
15 if (this.health < 0) this.health = 0;
16 return `Zdrowie ${this.name} zaktualizowane do ${this.health}%`;
17};
18
19console.log(triceratops.updateHealth(-15)); // "Zdrowie Trixie zaktualizowane do 80%"
20
21// Usuwanie właściwości
22delete triceratops.age;
23console.log(triceratops.age); // undefinedGdy potrzebujemy stworzyć wiele podobnych obiektów, efektywniejszym podejściem jest użycie funkcji konstruktora i operatora
new.1// Funkcja konstruktora dla dinozaurów
2function Dinosaur(name, species, age, diet) {
3 this.name = name;
4 this.species = species;
5 this.age = age;
6 this.diet = diet;
7 this.health = 100;
8 this.hunger = 50;
9
10 // Metoda wspólna dla wszystkich dinozaurów
11 this.feed = function() {
12 this.hunger -= 30;
13 if (this.hunger < 0) this.hunger = 0;
14 return `${this.name} został(a) nakarmiony(a). Poziom głodu: ${this.hunger}%`;
15 };
16}
17
18// Tworzenie nowych instancji dinozaurów
19const blue = new Dinosaur("Blue", "Velociraptor", 4, "carnivore");
20const trixie = new Dinosaur("Trixie", "Triceratops", 6, "herbivore");
21const rex = new Dinosaur("Rex", "Tyrannosaurus", 8, "carnivore");
22
23console.log(blue.species); // "Velociraptor"
24console.log(trixie.diet); // "herbivore"
25console.log(rex.feed()); // "Rex został(a) nakarmiony(a). Poziom głodu: 20%"Funkcja konstruktora tworzy "szablon" dla obiektów, a operator
new tworzy nową instancję obiektu zgodnie z tym szablonem.Możemy sprawdzić, czy obiekt został utworzony za pomocą konkretnego konstruktora:
1console.log(blue instanceof Dinosaur); // true
2console.log(velociraptor instanceof Dinosaur); // false (został utworzony jako literał)W nowszych wersjach JavaScript (ES6+) wprowadzono klasy, które zapewniają bardziej przyjazną składnię dla programowania obiektowego. Pod maską nadal są to funkcje konstruktora, ale z bardziej czytelną składnią.
1class Dinosaur {
2 constructor(name, species, age, diet) {
3 this.name = name;
4 this.species = species;
5 this.age = age;
6 this.diet = diet;
7 this.health = 100;
8 this.hunger = 50;
9 this.isAsleep = false;
10 }
11
12 // Metody są automatycznie dodawane do prototypu
13 feed() {
14 if (this.isAsleep) {
15 return `${this.name} śpi. Lepiej go/jej nie budzić podczas karmienia.`;
16 }
17
18 this.hunger -= 30;
19 if (this.hunger < 0) this.hunger = 0;
20 return `${this.name} został(a) nakarmiony(a). Poziom głodu: ${this.hunger}%`;
21 }
22
23 sleep() {
24 this.isAsleep = true;
25 return `${this.name} zasypia.`;
26 }
27
28 wakeUp() {
29 this.isAsleep = false;
30 return `${this.name} budzi się.`;
31 }
32
33 getStatus() {
34 return `
35Dinozaur: ${this.name} (Gatunek: ${this.species})
36Wiek: ${this.age} lat
37Dieta: ${this.diet === "carnivore" ? "Mięsożerna" : "Roślinożerna"}
38Status zdrowia: ${this.health}%
39Poziom głodu: ${this.hunger}%
40Stan: ${this.isAsleep ? "Śpi" : "Aktywny"}`;
41 }
42}
43
44// Tworzenie instancji klasy
45const blue = new Dinosaur("Blue", "Velociraptor", 4, "carnivore");
46console.log(blue.getStatus());
47
48console.log(blue.sleep()); // "Blue zasypia."
49console.log(blue.feed()); // "Blue śpi. Lepiej go/jej nie budzić podczas karmienia."
50console.log(blue.wakeUp()); // "Blue budzi się."
51console.log(blue.feed()); // "Blue został(a) nakarmiony(a). Poziom głodu: 20%"Klasy umożliwiają łatwe implementowanie dziedziczenia:
1// Klasa bazowa
2class Animal {
3 constructor(name, species) {
4 this.name = name;
5 this.species = species;
6 this.alive = true;
7 }
8
9 makeSound() {
10 return "Zwierzę wydaje dźwięk";
11 }
12}
13
14// Klasa pochodna dziedzicząca z Animal
15class Dinosaur extends Animal {
16 constructor(name, species, diet) {
17 // Wywołujemy konstruktor klasy bazowej
18 super(name, species);
19 this.diet = diet;
20 this.health = 100;
21 }
22
23 // Nadpisujemy metodę z klasy bazowej
24 makeSound() {
25 if (this.diet === "carnivore") {
26 return "ROAR!!! Przerażający ryk!";
27 } else {
28 return "Łagodny, niski pomruk.";
29 }
30 }
31
32 // Dodajemy nowe metody
33 eat(food) {
34 if (this.diet === "carnivore" && food.type === "meat") {
35 return `${this.name} zjada ${food.name} z apetytem!`;
36 } else if (this.diet === "herbivore" && food.type === "plant") {
37 return `${this.name} skubie ${food.name} zadowolony.`;
38 } else {
39 return `${this.name} nie jest zainteresowany jedzeniem ${food.name}.`;
40 }
41 }
42}
43
44// Obiekty reprezentujące jedzenie
45const meat = { name: "mięso", type: "meat" };
46const plants = { name: "rośliny", type: "plant" };
47
48// Tworzymy instancje dinozaurów
49const rex = new Dinosaur("Rex", "Tyrannosaurus", "carnivore");
50const spike = new Dinosaur("Spike", "Stegosaurus", "herbivore");
51
52console.log(rex.makeSound()); // "ROAR!!! Przerażający ryk!"
53console.log(spike.makeSound()); // "Łagodny, niski pomruk."
54
55console.log(rex.eat(meat)); // "Rex zjada mięso z apetytem!"
56console.log(rex.eat(plants)); // "Rex nie jest zainteresowany jedzeniem rośliny."
57console.log(spike.eat(plants)); // "Spike skubie rośliny zadowolony."Metody statyczne są wywoływane na samej klasie, a nie na jej instancjach:
1class DinosaurDatabase {
2 static dinoCount = 0;
3
4 constructor(name, species) {
5 this.name = name;
6 this.species = species;
7 DinosaurDatabase.dinoCount++;
8 }
9
10 // Metoda instancji - wymaga utworzenia obiektu
11 getInfo() {
12 return `${this.name} (${this.species})`;
13 }
14
15 // Metoda statyczna - wywoływana na klasie, nie na instancji
16 static getTotalDinosaurCount() {
17 return `Całkowita liczba dinozaurów w bazie: ${DinosaurDatabase.dinoCount}`;
18 }
19
20 static compareAge(dino1, dino2) {
21 if (!dino1.age || !dino2.age) return "Brak danych o wieku.";
22
23 if (dino1.age > dino2.age) {
24 return `${dino1.name} jest starszy od ${dino2.name}.`;
25 } else if (dino1.age < dino2.age) {
26 return `${dino1.name} jest młodszy od ${dino2.name}.`;
27 } else {
28 return `${dino1.name} i ${dino2.name} są w tym samym wieku.`;
29 }
30 }
31}
32
33const dino1 = new DinosaurDatabase("Rex", "Tyrannosaurus");
34dino1.age = 8;
35
36const dino2 = new DinosaurDatabase("Blue", "Velociraptor");
37dino2.age = 4;
38
39const dino3 = new DinosaurDatabase("Trixie", "Triceratops");
40dino3.age = 6;
41
42console.log(DinosaurDatabase.getTotalDinosaurCount()); // "Całkowita liczba dinozaurów w bazie: 3"
43console.log(DinosaurDatabase.compareAge(dino1, dino2)); // "Rex jest starszy od Blue."
44console.log(DinosaurDatabase.compareAge(dino2, dino3)); // "Blue jest młodszy od Trixie."Metoda
Object.create() pozwala na tworzenie nowych obiektów z określonym prototypem:1// Obiekt prototypowy dla wszystkich dinozaurów
2const dinoPrototype = {
3 makeSound() {
4 return "Ryk!";
5 },
6 eat() {
7 return `${this.name} je.`;
8 }
9};
10
11// Tworzenie obiektu z prototypem dinoPrototype
12const rex = Object.create(dinoPrototype);
13rex.name = "Rex";
14rex.species = "Tyrannosaurus";
15
16console.log(rex.name); // "Rex"
17console.log(rex.makeSound()); // "Ryk!"
18console.log(rex.eat()); // "Rex je."Funkcje fabryczne to funkcje, które tworzą i zwracają obiekty bez użycia
new:1// Funkcja fabryczna do tworzenia obiektów dinozaurów
2function createDinosaur(name, species, diet) {
3 return {
4 name, // Skrócona składnia dla name: name w ES6+
5 species,
6 diet,
7 health: 100,
8 hunger: 50,
9
10 feed() {
11 this.hunger -= 30;
12 if (this.hunger < 0) this.hunger = 0;
13 return `${this.name} został(a) nakarmiony(a). Poziom głodu: ${this.hunger}%`;
14 },
15
16 getStatus() {
17 return `${this.name} (${this.species}) - Zdrowie: ${this.health}%, Głód: ${this.hunger}%`;
18 }
19 };
20}
21
22const blue = createDinosaur("Blue", "Velociraptor", "carnivore");
23const trixie = createDinosaur("Trixie", "Triceratops", "herbivore");
24
25console.log(blue.getStatus()); // "Blue (Velociraptor) - Zdrowie: 100%, Głód: 50%"
26console.log(blue.feed()); // "Blue został(a) nakarmiony(a). Poziom głodu: 20%"1class DinosaurManager {
2 constructor() {
3 this.dinosaurs = [];
4 this.enclosures = [];
5 }
6
7 addDinosaur(dino) {
8 this.dinosaurs.push(dino);
9 console.log(`Dodano nowego dinozaura: ${dino.name} (${dino.species})`);
10 }
11
12 addEnclosure(enclosure) {
13 this.enclosures.push(enclosure);
14 console.log(`Dodano nowe wybieg: ${enclosure.name} (Pojemność: ${enclosure.capacity})`);
15 }
16
17 assignDinosaurToEnclosure(dinoName, enclosureName) {
18 const dino = this.dinosaurs.find(d => d.name === dinoName);
19 const enclosure = this.enclosures.find(e => e.name === enclosureName);
20
21 if (!dino) {
22 return `Błąd: Dinozaur o nazwie ${dinoName} nie znaleziony.`;
23 }
24
25 if (!enclosure) {
26 return `Błąd: Wybieg o nazwie ${enclosureName} nie znaleziony.`;
27 }
28
29 if (enclosure.dinosaurs.length >= enclosure.capacity) {
30 return `Błąd: Wybieg ${enclosureName} osiągnął maksymalną pojemność.`;
31 }
32
33 // Sprawdzenie czy drapieżniki nie są mieszane z roślinożercami
34 if (enclosure.dinosaurs.length > 0) {
35 const existingDiet = enclosure.dinosaurs[0].diet;
36 if (dino.diet === "carnivore" && existingDiet === "herbivore") {
37 return "Błąd: Nie można umieszczać drapieżników z roślinożercami!";
38 }
39 if (dino.diet === "herbivore" && existingDiet === "carnivore") {
40 return "Błąd: Nie można umieszczać roślinożerców z drapieżnikami!";
41 }
42 }
43
44 // Usunięcie dinozaura z poprzedniego wybiegu, jeśli był przypisany
45 this.enclosures.forEach(e => {
46 const index = e.dinosaurs.findIndex(d => d.name === dinoName);
47 if (index !== -1) {
48 e.dinosaurs.splice(index, 1);
49 }
50 });
51
52 // Przypisanie dinozaura do nowego wybiegu
53 enclosure.dinosaurs.push(dino);
54 dino.enclosure = enclosureName;
55
56 return `${dinoName} został(a) przypisany(a) do wybiegu ${enclosureName}.`;
57 }
58
59 getEnclosureReport(enclosureName) {
60 const enclosure = this.enclosures.find(e => e.name === enclosureName);
61
62 if (!enclosure) {
63 return `Błąd: Wybieg o nazwie ${enclosureName} nie znaleziony.`;
64 }
65
66 let report = `=== RAPORT DLA WYBIEGU: ${enclosure.name} ===
67Pojemność: ${enclosure.dinosaurs.length}/${enclosure.capacity}
68Status: ${enclosure.status}
69Dinozaury:
70`;
71
72 if (enclosure.dinosaurs.length === 0) {
73 report += "Brak dinozaurów w tym wybiegu.";
74 } else {
75 enclosure.dinosaurs.forEach(dino => {
76 report += `- ${dino.name} (${dino.species}): Zdrowie: ${dino.health}%, Głód: ${dino.hunger}%
77`;
78 });
79 }
80
81 return report;
82 }
83}
84
85// Klasa dla dinozaurów
86class Dinosaur {
87 constructor(name, species, diet) {
88 this.name = name;
89 this.species = species;
90 this.diet = diet;
91 this.health = 100;
92 this.hunger = 50;
93 this.enclosure = null; // Nazwa wybiegu, do którego przypisany jest dinozaur
94 }
95}
96
97// Klasa dla wybiegów
98class Enclosure {
99 constructor(name, capacity, security) {
100 this.name = name;
101 this.capacity = capacity;
102 this.security = security; // poziom zabezpieczeń
103 this.status = "Operational";
104 this.dinosaurs = []; // Lista dinozaurów w wybiegu
105 }
106}
107
108// Użycie systemu zarządzania
109const parkManager = new DinosaurManager();
110
111// Tworzenie wybiegów
112const carnivoreEnclosure = new Enclosure("Paddock A", 3, "Maximum");
113const herbivoreEnclosure = new Enclosure("Paddock B", 5, "Standard");
114
115parkManager.addEnclosure(carnivoreEnclosure);
116parkManager.addEnclosure(herbivoreEnclosure);
117
118// Tworzenie dinozaurów
119const rex = new Dinosaur("Rex", "Tyrannosaurus", "carnivore");
120const blue = new Dinosaur("Blue", "Velociraptor", "carnivore");
121const trixie = new Dinosaur("Trixie", "Triceratops", "herbivore");
122const spike = new Dinosaur("Spike", "Stegosaurus", "herbivore");
123
124parkManager.addDinosaur(rex);
125parkManager.addDinosaur(blue);
126parkManager.addDinosaur(trixie);
127parkManager.addDinosaur(spike);
128
129// Przypisywanie dinozaurów do wybiegów
130console.log(parkManager.assignDinosaurToEnclosure("Rex", "Paddock A"));
131console.log(parkManager.assignDinosaurToEnclosure("Blue", "Paddock A"));
132console.log(parkManager.assignDinosaurToEnclosure("Trixie", "Paddock B"));
133console.log(parkManager.assignDinosaurToEnclosure("Spike", "Paddock B"));
134
135// Próba przypisania roślinożercy do wybiegu drapieżników
136console.log(parkManager.assignDinosaurToEnclosure("Trixie", "Paddock A"));
137
138// Wyświetlanie raportów
139console.log(parkManager.getEnclosureReport("Paddock A"));
140console.log(parkManager.getEnclosureReport("Paddock B"));Obiekty są fundamentalnym elementem języka JavaScript i pozwalają nam modelować złożone struktury danych i zachowania. W Parku Jurajskim używamy ich do reprezentowania wszystkiego - od dinozaurów po system zarządzania całym parkiem.
Mamy kilka sposobów tworzenia obiektów:
Wybór konkretnej techniki zależy od sytuacji i potrzeb projektu. W złożonych aplikacjach, jak nasz system zarządzania Parkiem Jurajskim, najczęściej używamy kombinacji tych technik.