Kurs JavaScript i TypeScript · Moduł 2: Funkcje, tablice i obiekty

Tworzenie obiektów (literały, konstruktory)

10 min czytania
W tej lekcji8

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.

Literały obiektów

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); // 64

Dostęp do właściwości

Moż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"

Metody obiektów

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.

Dynamiczne dodawanie i usuwanie właściwości

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); // undefined

Konstruktory i operator new

Gdy 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.

Zalety konstruktorów:

  1. Spójność - wszystkie obiekty mają ten sam zestaw właściwości i metod
  2. Skalowalność - łatwiej tworzyć wiele podobnych obiektów
  3. Czytelność - kod staje się bardziej przewidywalny i łatwy do utrzymania

Sprawdzanie typu obiektu - operator instanceof

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ł)

Klasy (ES6+)

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%"

Dziedziczenie za pomocą klas

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

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."

Object.create()

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."

Factory Functions (Funkcje fabryczne)

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%"

Praktyczne zastosowanie obiektów w Parku Jurajskim

System zarządzania dinozaurami

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 nowy 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:\n`;
70
71    if (enclosure.dinosaurs.length === 0) {
72      report += "Brak dinozaurów w tym wybiegu.";
73    } else {
74      enclosure.dinosaurs.forEach(dino => {
75        report += `- ${dino.name} (${dino.species}): Zdrowie: ${dino.health}%, Głód: ${dino.hunger}%\n`;
76      });
77    }
78
79    return report;
80  }
81}
82
83// Klasa dla dinozaurów
84class Dinosaur {
85  constructor(name, species, diet) {
86    this.name = name;
87    this.species = species;
88    this.diet = diet;
89    this.health = 100;
90    this.hunger = 50;
91    this.enclosure = null; // Nazwa wybiegu, do którego przypisany jest dinozaur
92  }
93}
94
95// Klasa dla wybiegów
96class Enclosure {
97  constructor(name, capacity, security) {
98    this.name = name;
99    this.capacity = capacity;
100    this.security = security; // poziom zabezpieczeń
101    this.status = "Operational";
102    this.dinosaurs = []; // Lista dinozaurów w wybiegu
103  }
104}
105
106// Użycie systemu zarządzania
107const parkManager = new DinosaurManager();
108
109// Tworzenie wybiegów
110const carnivoreEnclosure = new Enclosure("Paddock A", 3, "Maximum");
111const herbivoreEnclosure = new Enclosure("Paddock B", 5, "Standard");
112
113parkManager.addEnclosure(carnivoreEnclosure);
114parkManager.addEnclosure(herbivoreEnclosure);
115
116// Tworzenie dinozaurów
117const rex = new Dinosaur("Rex", "Tyrannosaurus", "carnivore");
118const blue = new Dinosaur("Blue", "Velociraptor", "carnivore");
119const trixie = new Dinosaur("Trixie", "Triceratops", "herbivore");
120const spike = new Dinosaur("Spike", "Stegosaurus", "herbivore");
121
122parkManager.addDinosaur(rex);
123parkManager.addDinosaur(blue);
124parkManager.addDinosaur(trixie);
125parkManager.addDinosaur(spike);
126
127// Przypisywanie dinozaurów do wybiegów
128console.log(parkManager.assignDinosaurToEnclosure("Rex", "Paddock A"));
129console.log(parkManager.assignDinosaurToEnclosure("Blue", "Paddock A"));
130console.log(parkManager.assignDinosaurToEnclosure("Trixie", "Paddock B"));
131console.log(parkManager.assignDinosaurToEnclosure("Spike", "Paddock B"));
132
133// Próba przypisania roślinożercy do wybiegu drapieżników
134console.log(parkManager.assignDinosaurToEnclosure("Trixie", "Paddock A"));
135
136// Wyświetlanie raportów
137console.log(parkManager.getEnclosureReport("Paddock A"));
138console.log(parkManager.getEnclosureReport("Paddock B"));

Podsumowanie

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:

  1. Literały obiektów - najprostszy sposób, idealny dla pojedynczych, unikalnych obiektów
  2. Funkcje konstruktora - gdy potrzebujemy tworzyć wiele podobnych obiektów
  3. Klasy (ES6+) - nowoczesna, czytelna składnia do tworzenia szablonów obiektów z dziedziczeniem
  4. Object.create() - tworzenie obiektów z określonym prototypem
  5. Funkcje fabryczne - funkcje zwracające nowe obiekty, alternatywa dla konstruktoró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.

Kod do tej lekcji: index.js
1// Obiekty - tworzenie - Park Jurajski
2console.log("Laboratorium Obiektów JavaScript");
3console.log("Tworzenie i konstruowanie obiektów\n");
4
5// ===========================================
6// 1. LITERAŁY OBIEKTOWE (najprostszy sposób)
7// ===========================================
8console.log("=== 1. Literały obiektowe ===");
9
10// Podstawowy obiekt
11const dinosaur1 = {
12  name: "T-Rex",
13  species: "Tyrannosaurus Rex",
14  age: 8,
15  weight: 8000,
16  carnivore: true
17};
18
19console.log("Dinozaur 1:");
20console.log(`   Imię: ${dinosaur1.name}`);
21console.log(`   Gatunek: ${dinosaur1.species}`);
22console.log(`   Wiek: ${dinosaur1.age} lat`);
23
24// Obiekt z metodami
25const dinosaur2 = {
26  name: "Blue",
27  species: "Velociraptor",
28  speed: 40,
29
30  // Metoda tradycyjna
31  roar: function() {
32    return `${this.name} ryczy: SHREEK!`;
33  },
34
35  // Metoda skrócona (ES6)
36  run() {
37    return `${this.name} biegnie z prędkością ${this.speed} km/h`;
38  }
39};
40
41console.log("\nDinozaur 2:");
42console.log(`   ${dinosaur2.roar()}`);
43console.log(`   ${dinosaur2.run()}`);
44
45// ===========================================
46// 2. KONSTRUKTORY FUNKCYJNE
47// ===========================================
48console.log("\n=== 2. Konstruktory funkcyjne ===");
49
50function Dinosaur(name, species, age, weight, carnivore) {
51  this.name = name;
52  this.species = species;
53  this.age = age;
54  this.weight = weight;
55  this.carnivore = carnivore;
56
57  this.getInfo = function() {
58    return `${this.name} (${this.species}) - ${this.age} lat, ${this.weight}kg`;
59  };
60
61  this.roar = function() {
62    return `${this.name} says: ROAARRR!`;
63  };
64}
65
66const trex = new Dinosaur("Rexy", "T-Rex", 8, 8000, true);
67const trike = new Dinosaur("Trike", "Triceratops", 7, 9000, false);
68
69console.log("\nUtworzono przez konstruktor:");
70console.log(`   ${trex.getInfo()}`);
71console.log(`   ${trex.roar()}`);
72console.log(`   ${trike.getInfo()}`);
73
74// ===========================================
75// 3. Object.create()
76// ===========================================
77console.log("\n=== 3. Object.create() ===");
78
79const dinoPrototype = {
80  getInfo() {
81    return `${this.name} (${this.species})`;
82  },
83
84  roar() {
85    return `${this.name}: ROAARRR!`;
86  }
87};
88
89const velociraptor = Object.create(dinoPrototype);
90velociraptor.name = "Charlie";
91velociraptor.species = "Velociraptor";
92velociraptor.age = 5;
93
94console.log("\nUtworzono przez Object.create():");
95console.log(`   ${velociraptor.getInfo()}`);
96console.log(`   ${velociraptor.roar()}`);
97
98// ===========================================
99// 4. KLASY ES6
100// ===========================================
101console.log("\n=== 4. Klasy ES6 ===");
102
103class DinosaurClass {
104  constructor(name, species, age, weight, carnivore) {
105    this.name = name;
106    this.species = species;
107    this.age = age;
108    this.weight = weight;
109    this.carnivore = carnivore;
110  }
111
112  getInfo() {
113    return `${this.name} (${this.species}) - ${this.age} lat, ${this.weight}kg`;
114  }
115
116  roar() {
117    return `${this.name} says: ROAARRR!`;
118  }
119
120  isHeavy() {
121    return this.weight > 1000;
122  }
123}
124
125const stego = new DinosaurClass("Stego", "Stegosaurus", 6, 5000, false);
126const brachio = new DinosaurClass("Brachio", "Brachiosaurus", 10, 50000, false);
127
128console.log("\nUtworzono przez klasę:");
129console.log(`   ${stego.getInfo()}`);
130console.log(`   Czy ciężki? ${stego.isHeavy()}`);
131console.log(`   ${brachio.getInfo()}`);
132console.log(`   Czy ciężki? ${brachio.isHeavy()}`);
133
134// ===========================================
135// 5. FACTORY FUNCTIONS
136// ===========================================
137console.log("\n=== 5. Factory functions ===");
138
139function createDinosaur(name, species, options = {}) {
140  return {
141    name,
142    species,
143    age: options.age || 5,
144    weight: options.weight || 100,
145    carnivore: options.carnivore || false,
146    sector: options.sector || "A",
147
148    getInfo() {
149      return `${this.name} (${this.species}) - Sektor ${this.sector}`;
150    },
151
152    feed() {
153      console.log(`Karmienie ${this.name}`);
154    }
155  };
156}
157
158const dilo = createDinosaur("Dilo", "Dilophosaurus", {
159  age: 4,
160  weight: 400,
161  carnivore: true,
162  sector: "E"
163});
164
165console.log("\nUtworzono przez factory:");
166console.log(`   ${dilo.getInfo()}`);
167dilo.feed();
168
169// ===========================================
170// 6. OBIEKTY ZAGNIEŻDŻONE
171// ===========================================
172console.log("\n=== 6. Obiekty zagnieżdżone ===");
173
174const park = {
175  name: "Jurassic Park",
176  location: "Isla Nublar",
177
178  sectors: {
179    A: { name: "Carnivore Zone", capacity: 5, current: 2 },
180    B: { name: "Raptor Pen", capacity: 10, current: 3 },
181    C: { name: "Herbivore Valley", capacity: 20, current: 8 }
182  },
183
184  stats: {
185    totalDinosaurs: 13,
186    totalVisitors: 1500,
187    securityLevel: "HIGH"
188  },
189
190  getReport() {
191    console.log(`\nRaport parku ${this.name}:`);
192    console.log(`   Lokalizacja: ${this.location}`);
193    console.log(`   Dinozaury: ${this.stats.totalDinosaurs}`);
194    console.log(`   Odwiedzający: ${this.stats.totalVisitors}`);
195    console.log(`   Bezpieczeństwo: ${this.stats.securityLevel}`);
196  }
197};
198
199park.getReport();
200
201console.log("\nSektory:");
202Object.entries(park.sectors).forEach(([key, sector]) => {
203  console.log(`   ${key}: ${sector.name} - ${sector.current}/${sector.capacity}`);
204});
205
206// ===========================================
207// 7. GETTERY I SETTERY
208// ===========================================
209console.log("\n=== 7. Gettery i settery ===");
210
211class ManagedDinosaur {
212  constructor(name, species, health) {
213    this.name = name;
214    this.species = species;
215    this._health = health; // Prywatna właściwość (konwencja)
216  }
217
218  // Getter
219  get health() {
220    return this._health;
221  }
222
223  // Setter z walidacją
224  set health(value) {
225    if (value < 0) value = 0;
226    if (value > 100) value = 100;
227    this._health = value;
228    console.log(`   Zdrowie ${this.name} zmienione na ${this._health}%`);
229  }
230
231  get status() {
232    if (this._health >= 80) return "Zdrowy";
233    if (this._health >= 50) return "Średni";
234    return "Krytyczny";
235  }
236}
237
238const managedDino = new ManagedDinosaur("Rex", "T-Rex", 75);
239console.log(`\n${managedDino.name}:`);
240console.log(`   Zdrowie: ${managedDino.health}%`);
241console.log(`   Status: ${managedDino.status}`);
242
243managedDino.health = 95;
244console.log(`   Nowy status: ${managedDino.status}`);
245
246managedDino.health = 150; // Będzie ograniczone do 100
247console.log(`   Status po przekroczeniu: ${managedDino.status}`);
248
249// ===========================================
250// 8. STATYCZNE METODY I WŁAŚCIWOŚCI
251// ===========================================
252console.log("\n=== 8. Statyczne metody i właściwości ===");
253
254class DinoSpecies {
255  static species = [
256    "Tyrannosaurus Rex",
257    "Velociraptor",
258    "Triceratops",
259    "Stegosaurus",
260    "Brachiosaurus"
261  ];
262
263  static isCarnivore(species) {
264    const carnivores = ["Tyrannosaurus Rex", "Velociraptor", "Spinosaurus"];
265    return carnivores.includes(species);
266  }
267
268  static getInfo(species) {
269    const info = {
270      "Tyrannosaurus Rex": { weight: 8000, period: "Kredowy" },
271      "Velociraptor": { weight: 80, period: "Kredowy" },
272      "Triceratops": { weight: 9000, period: "Kredowy" }
273    };
274    return info[species] || { weight: "?", period: "?" };
275  }
276
277  static listAll() {
278    console.log("\nWszystkie gatunki:");
279    this.species.forEach((s, i) => {
280      const carnivore = this.isCarnivore(s) ? "mięsożerca" : "roślinożerca";
281      console.log(`   ${i + 1}. ${carnivore} ${s}`);
282    });
283  }
284}
285
286DinoSpecies.listAll();
287
288console.log("\nSprawdzanie gatunków:");
289console.log(`   T-Rex mięsożerca? ${DinoSpecies.isCarnivore("Tyrannosaurus Rex")}`);
290console.log(`   Triceratops mięsożerca? ${DinoSpecies.isCarnivore("Triceratops")}`);
291
292const trexInfo = DinoSpecies.getInfo("Tyrannosaurus Rex");
293console.log(`\nInfo o T-Rex: waga ${trexInfo.weight}kg, okres ${trexInfo.period}`);
294
295// ===========================================
296// 9. DZIEDZICZENIE
297// ===========================================
298console.log("\n=== 9. Dziedziczenie ===");
299
300class Animal {
301  constructor(name, age) {
302    this.name = name;
303    this.age = age;
304  }
305
306  breathe() {
307    return `${this.name} oddycha`;
308  }
309
310  getAge() {
311    return `${this.name} ma ${this.age} lat`;
312  }
313}
314
315class Carnivore extends Animal {
316  constructor(name, age, huntingSkill) {
317    super(name, age);
318    this.huntingSkill = huntingSkill;
319    this.type = "Mięsożerca";
320  }
321
322  hunt() {
323    return `${this.name} poluje (umiejętność: ${this.huntingSkill}/10)`;
324  }
325
326  roar() {
327    return `${this.name} ryczy: ROAARRR!`;
328  }
329}
330
331class Herbivore extends Animal {
332  constructor(name, age, plantPreference) {
333    super(name, age);
334    this.plantPreference = plantPreference;
335    this.type = "Roślinożerca";
336  }
337
338  eat() {
339    return `${this.name} je ${this.plantPreference}`;
340  }
341
342  graze() {
343    return `${this.name} spokojnie się pasie`;
344  }
345}
346
347const rex = new Carnivore("Rexy", 8, 10);
348const trico = new Herbivore("Trico", 7, "paprocie");
349
350console.log("\nMięsożerca:");
351console.log(`   ${rex.getAge()}`);
352console.log(`   ${rex.breathe()}`);
353console.log(`   ${rex.hunt()}`);
354console.log(`   ${rex.roar()}`);
355
356console.log("\nRoślinożerca:");
357console.log(`   ${trico.getAge()}`);
358console.log(`   ${trico.breathe()}`);
359console.log(`   ${trico.eat()}`);
360console.log(`   ${trico.graze()}`);
361
362// ===========================================
363// 10. PRZYKŁAD KOMPLEKSOWY
364// ===========================================
365console.log("\n=== 10. System zarządzania - przykład kompleksowy ===");
366
367class ParkManagementSystem {
368  constructor(parkName) {
369    this.parkName = parkName;
370    this.dinosaurs = new Map();
371    this.nextId = 1;
372  }
373
374  addDinosaur(name, species, options = {}) {
375    const dino = {
376      id: this.nextId++,
377      name,
378      species,
379      age: options.age || 5,
380      weight: options.weight || 1000,
381      health: options.health || 100,
382      sector: options.sector || "A",
383      carnivore: options.carnivore || false,
384      fed: false,
385      lastCheckup: new Date()
386    };
387
388    this.dinosaurs.set(dino.id, dino);
389    console.log(`Dodano: ${name} (#${dino.id})`);
390    return dino.id;
391  }
392
393  getDinosaur(id) {
394    return this.dinosaurs.get(id);
395  }
396
397  removeDinosaur(id) {
398    const dino = this.dinosaurs.get(id);
399    if (dino) {
400      this.dinosaurs.delete(id);
401      console.log(`Usunięto: ${dino.name} (#${id})`);
402      return true;
403    }
404    return false;
405  }
406
407  feedDinosaur(id) {
408    const dino = this.dinosaurs.get(id);
409    if (dino) {
410      dino.fed = true;
411      console.log(`Nakarmiono: ${dino.name}`);
412    }
413  }
414
415  updateHealth(id, newHealth) {
416    const dino = this.dinosaurs.get(id);
417    if (dino) {
418      dino.health = Math.max(0, Math.min(100, newHealth));
419      console.log(`Zaktualizowano zdrowie ${dino.name}: ${dino.health}%`);
420    }
421  }
422
423  getStats() {
424    const dinos = Array.from(this.dinosaurs.values());
425
426    return {
427      total: dinos.length,
428      carnivores: dinos.filter(d => d.carnivore).length,
429      avgHealth: (dinos.reduce((sum, d) => sum + d.health, 0) / dinos.length).toFixed(1),
430      fed: dinos.filter(d => d.fed).length,
431      bySector: dinos.reduce((acc, d) => {
432        acc[d.sector] = (acc[d.sector] || 0) + 1;
433        return acc;
434      }, {})
435    };
436  }
437
438  displayReport() {
439    console.log(`\n============================================================`);
440    console.log(`RAPORT PARKU: ${this.parkName}`);
441    console.log("=".repeat(60));
442
443    const stats = this.getStats();
444    console.log("\nStatystyki:");
445    console.log(`   Łącznie dinozaurów: ${stats.total}`);
446    console.log(`   Mięsożercy: ${stats.carnivores}`);
447    console.log(`   Średnie zdrowie: ${stats.avgHealth}%`);
448    console.log(`   Nakarmione: ${stats.fed}/${stats.total}`);
449
450    console.log("\nPodział na sektory:");
451    Object.entries(stats.bySector).sort().forEach(([sector, count]) => {
452      console.log(`   Sektor ${sector}: ${count}`);
453    });
454
455    console.log("\nLista dinozaurów:");
456    Array.from(this.dinosaurs.values())
457      .sort((a, b) => a.id - b.id)
458      .forEach(dino => {
459        const icon = dino.carnivore ? "mięsożerca" : "roślinożerca";
460        const fedStatus = dino.fed ? "√" : "×";
461        console.log(`   #${dino.id} ${icon} ${dino.name} (${dino.species})`);
462        console.log(`       Sektor ${dino.sector} | Zdrowie: ${dino.health}% | Nakarmiony: ${fedStatus}`);
463      });
464  }
465}
466
467// Demonstracja systemu
468console.log();
469const parkSystem = new ParkManagementSystem("Isla Nublar");
470
471const id1 = parkSystem.addDinosaur("Rexy", "T-Rex", { age: 8, weight: 8000, sector: "A", carnivore: true });
472const id2 = parkSystem.addDinosaur("Blue", "Velociraptor", { age: 5, weight: 80, sector: "B", carnivore: true, health: 100 });
473const id3 = parkSystem.addDinosaur("Charlie", "Velociraptor", { age: 5, weight: 80, sector: "B", carnivore: true, health: 85 });
474const id4 = parkSystem.addDinosaur("Trike", "Triceratops", { age: 7, weight: 9000, sector: "C", health: 70 });
475const id5 = parkSystem.addDinosaur("Stego", "Stegosaurus", { age: 6, weight: 5000, sector: "C" });
476
477console.log();
478parkSystem.feedDinosaur(id1);
479parkSystem.feedDinosaur(id2);
480parkSystem.updateHealth(id4, 85);
481
482parkSystem.displayReport();
483
484console.log("\nGratulacje! Opanowałeś tworzenie 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. 1. Jakie są dwa sposoby dostępu do właściwości obiektu w JavaScript?

  2. 2. Co robi słowo kluczowe 'new' przed wywołaniem funkcji konstruktora?

Zadania praktyczne w grze

  • Klikanie w kolejności

    Ułóż elementy tworzenia obiektu dinozaura z metodą:

  • Edytor kodu

    Utwórz obiekt dinosaur z właściwościami name: 'Rexy', species: 'Tyrannosaurus Rex', age: 25 i isCarnivore: true oraz z zagnieżdżonym obiektem stats, który ma właściwość health: 95. Po utworzeniu obiektu dodaj do niego właściwość enclosure o wartości 'Zone A'. Wyświetl w konsoli name w notacji kropkowej i species w notacji nawiasowej.

  • Układanie w poziomie

    Ułóż elementy tworzenia obiektu literałem:

  • Klikanie w kolejności

    Ułóż elementy deklaracji obiektu z właściwościami:

  • Układanie w poziomie

    Ułóż elementy deklaracji obiektu w JavaScript

Przydatne artykuły