Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Tworzenie obiektów (literały, konstruktory)

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 operatory 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 Park 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 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"));

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.

Vai a CodeWorlds