W Parku Jurajskim efektywne zarządzanie różnymi gatunkami dinozaurów wymaga zrozumienia podobieństw między nimi i możliwości dziedziczenia cech. JavaScript używa prototypów do implementacji mechanizmu dziedziczenia - jest to jeden z najbardziej unikalnych i potężnych aspektów tego języka. Zrozumienie prototypów i dziedziczenia prototypowego jest kluczowe dla pisania wydajnego, elastycznego kodu JavaScript.
W JavaScript każdy obiekt ma wewnętrzną właściwość zwaną prototypem, która jest referencją do innego obiektu. Gdy próbujemy uzyskać dostęp do właściwości obiektu, JavaScript najpierw sprawdza, czy sam obiekt posiada taką właściwość. Jeśli nie, sprawdza prototyp obiektu, następnie prototyp prototypu, itd., tworząc tzw. łańcuch prototypów.
1// Tworzymy prosty obiekt
2const dinosaur = {
3 species: "Tyrannosaurus",
4 makesSound: true,
5 roar() {
6 return "ROOOOAR!";
7 }
8};
9
10// Tworzymy nowy obiekt, który dziedziczy po obiekcie dinosaur
11const rex = Object.create(dinosaur);
12rex.name = "Rex";
13rex.age = 8;
14
15// rex ma dostęp do swoich własnych właściwości
16console.log(rex.name); // "Rex"
17console.log(rex.age); // 8
18
19// rex ma również dostęp do właściwości i metod dinosaur
20console.log(rex.species); // "Tyrannosaurus"
21console.log(rex.makesSound); // true
22console.log(rex.roar()); // "ROOOOAR!"
23
24// Sprawdźmy, czy rex ma własną właściwość 'species' (nie ma)
25console.log(rex.hasOwnProperty('species')); // false
26
27// Ale ma własną właściwość 'name'
28console.log(rex.hasOwnProperty('name')); // trueW powyższym przykładzie
rex nie ma własnej właściwości species, ale dostęp do rex.species działa, ponieważ JavaScript szuka tej właściwości w prototypie rex, którym jest obiekt dinosaur.Prototypy tworzą łańcuch, który JavaScript przeszukuje, gdy próbujemy uzyskać dostęp do właściwości. Ten mechanizm nazywamy łańcuchem prototypów (prototype chain).
1// Podstawowy obiekt przodek
2const animal = {
3 isAlive: true,
4 eat() {
5 return `${this.name} je.`;
6 }
7};
8
9// Obiekt dinozaur dziedziczący po animal
10const dinosaur = Object.create(animal);
11dinosaur.isExtinct = false;
12dinosaur.roar = function() {
13 return `${this.name} ryczy!`;
14};
15
16// Konkretny gatunek dinozaura dziedziczący po dinosaur
17const tyrannosaurus = Object.create(dinosaur);
18tyrannosaurus.name = "Tyrannosaurus";
19tyrannosaurus.diet = "carnivore";
20tyrannosaurus.length = 12; // metry
21
22// tyrannosaurus ma dostęp do właściwości wszystkich prototypów w łańcuchu
23console.log(tyrannosaurus.name); // "Tyrannosaurus" (własna właściwość)
24console.log(tyrannosaurus.isExtinct); // false (z dinosaur)
25console.log(tyrannosaurus.isAlive); // true (z animal)
26console.log(tyrannosaurus.eat()); // "Tyrannosaurus je." (metoda z animal, this odwołuje się do tyrannosaurus)
27console.log(tyrannosaurus.roar()); // "Tyrannosaurus ryczy!" (metoda z dinosaur)W praktyce, prototypy są najczęściej używane z konstruktorami i klasami do implementowania dziedziczenia w JavaScript.
Każda funkcja konstruktora ma właściwość
prototype, która jest używana jako prototyp dla obiektów tworzonych za pomocą tej funkcji z operatorem new.1// Funkcja konstruktora dla Dinosaur
2function Dinosaur(name, species, diet) {
3 this.name = name;
4 this.species = species;
5 this.diet = diet;
6 this.isExtinct = false;
7}
8
9// Dodajemy metody do prototypu Dinosaur
10Dinosaur.prototype.roar = function() {
11 const volume = this.diet === "carnivore" ? "głośno" : "cicho";
12 return `${this.name} ryczy ${volume}!`;
13};
14
15Dinosaur.prototype.describe = function() {
16 return `${this.name} to ${this.species}, który jest ${this.diet === "carnivore" ? "mięsożerny" : "roślinożerny"}.`;
17};
18
19// Tworzymy instancje
20const trex = new Dinosaur("Rex", "Tyrannosaurus", "carnivore");
21const tricera = new Dinosaur("Trixie", "Triceratops", "herbivore");
22
23console.log(trex.roar()); // "Rex ryczy głośno!"
24console.log(tricera.roar()); // "Trixie ryczy cicho!"
25console.log(trex.describe()); // "Rex to Tyrannosaurus, który jest mięsożerny."
26
27// Wszystkie instancje współdzielą te same metody prototypu
28console.log(trex.roar === tricera.roar); // trueW tym przykładzie, metody
roar i describe są definiowane na prototypie funkcji konstruktora Dinosaur. Wszystkie instancje utworzone za pomocą new Dinosaur() współdzielą te same metody, co jest znacznie bardziej efektywne niż definiowanie tych metod w konstruktorze dla każdej instancji osobno.Możemy sprawdzić prototyp obiektu za pomocą kilku metod:
1// Używając Object.getPrototypeOf()
2console.log(Object.getPrototypeOf(trex) === Dinosaur.prototype); // true
3
4// Używając __proto__ (właściwość przestarzała, nie zalecana w kodzie produkcyjnym)
5console.log(trex.__proto__ === Dinosaur.prototype); // true
6
7// Sprawdzanie, czy obiekt jest instancją konstruktora
8console.log(trex instanceof Dinosaur); // true
9console.log(trex instanceof Object); // true (wszystkie obiekty dziedziczą po Object.prototype)Możemy implementować dziedziczenie między funkcjami konstruktora, łącząc ich prototypy:
1// Funkcja konstruktora dla Animal (klasa bazowa)
2function Animal(name) {
3 this.name = name;
4 this.isAlive = true;
5}
6
7Animal.prototype.eat = function(food) {
8 return `${this.name} je ${food}.`;
9};
10
11Animal.prototype.sleep = function() {
12 return `${this.name} śpi.`;
13};
14
15// Funkcja konstruktora dla Dinosaur (klasa pochodna)
16function Dinosaur(name, species, diet) {
17 // Wywołujemy konstruktor klasy bazowej
18 Animal.call(this, name);
19
20 // Dodajemy właściwości specyficzne dla Dinosaur
21 this.species = species;
22 this.diet = diet;
23 this.isExtinct = false;
24}
25
26// Ustawiamy prototyp Dinosaur, aby dziedziczył po Animal.prototype
27Dinosaur.prototype = Object.create(Animal.prototype);
28
29// Przywracamy właściwą właściwość konstruktora
30Dinosaur.prototype.constructor = Dinosaur;
31
32// Dodajemy metody specyficzne dla Dinosaur
33Dinosaur.prototype.roar = function() {
34 const volume = this.diet === "carnivore" ? "głośno" : "cicho";
35 return `${this.name} ryczy ${volume}!`;
36};
37
38// Tworzymy instancję Dinosaur
39const velociraptor = new Dinosaur("Blue", "Velociraptor", "carnivore");
40
41// Mamy dostęp do metod z Animal
42console.log(velociraptor.eat("mięso")); // "Blue je mięso."
43console.log(velociraptor.sleep()); // "Blue śpi."
44
45// Oraz do metod specyficznych dla Dinosaur
46console.log(velociraptor.roar()); // "Blue ryczy głośno!"
47
48// Sprawdzamy łańcuch prototypów
49console.log(velociraptor instanceof Dinosaur); // true
50console.log(velociraptor instanceof Animal); // true
51console.log(velociraptor instanceof Object); // trueTen wzorzec implementacji dziedziczenia między konstruktorami jest często nazywany "dziedziczeniem pseudoklasycznym", ponieważ emuluje dziedziczenie klasowe w języku, który oryginalnie bazuje na prototypach.
Możemy tworzyć wielopoziomowe hierarchie dziedziczenia, łącząc łańcuchy prototypów:
1// Klasa bazowa Animal
2function Animal(name) {
3 this.name = name;
4 this.isAlive = true;
5}
6
7Animal.prototype.breathe = function() {
8 return `${this.name} oddycha.`;
9};
10
11// Klasa Dinosaur dziedzicząca po Animal
12function Dinosaur(name, period) {
13 Animal.call(this, name);
14 this.period = period;
15 this.extinct = true;
16}
17
18Dinosaur.prototype = Object.create(Animal.prototype);
19Dinosaur.prototype.constructor = Dinosaur;
20
21Dinosaur.prototype.roar = function() {
22 return `${this.name} ryczy!`;
23};
24
25// Klasa Therapod dziedzicząca po Dinosaur
26function Therapod(name, period, speed) {
27 Dinosaur.call(this, name, period);
28 this.speed = speed; // km/h
29 this.bipedal = true;
30}
31
32Therapod.prototype = Object.create(Dinosaur.prototype);
33Therapod.prototype.constructor = Therapod;
34
35Therapod.prototype.run = function() {
36 return `${this.name} biegnie z prędkością ${this.speed} km/h!`;
37};
38
39// Klasa Tyrannosaurus dziedzicząca po Therapod
40function Tyrannosaurus(name, size) {
41 Therapod.call(this, name, "Late Cretaceous", 30);
42 this.size = size;
43 this.teeth = 60;
44}
45
46Tyrannosaurus.prototype = Object.create(Therapod.prototype);
47Tyrannosaurus.prototype.constructor = Tyrannosaurus;
48
49Tyrannosaurus.prototype.bite = function() {
50 return `${this.name} gryzie z ogromną siłą dzięki ${this.teeth} zębom!`;
51};
52
53// Tworzymy instancję Tyrannosaurus
54const rex = new Tyrannosaurus("Rex", "bardzo duży");
55
56// Mamy dostęp do metod wszystkich prototypów w łańcuchu
57console.log(rex.breathe()); // "Rex oddycha." (z Animal)
58console.log(rex.roar()); // "Rex ryczy!" (z Dinosaur)
59console.log(rex.run()); // "Rex biegnie z prędkością 30 km/h!" (z Therapod)
60console.log(rex.bite()); // "Rex gryzie z ogromną siłą dzięki 60 zębom!" (z Tyrannosaurus)
61
62// Sprawdzamy łańcuch prototypów
63console.log(rex instanceof Tyrannosaurus); // true
64console.log(rex instanceof Therapod); // true
65console.log(rex instanceof Dinosaur); // true
66console.log(rex instanceof Animal); // true
67console.log(rex instanceof Object); // trueES6 wprowadził składnię klas, która pod spodem używa prototypów, ale oferuje bardziej przyjazną, podobną do klasycznej obiektowości składnię:
1// Klasa bazowa Animal
2class Animal {
3 constructor(name) {
4 this.name = name;
5 this.isAlive = true;
6 }
7
8 eat(food) {
9 return `${this.name} je ${food}.`;
10 }
11
12 sleep() {
13 return `${this.name} śpi.`;
14 }
15}
16
17// Klasa Dinosaur dziedzicząca po Animal
18class Dinosaur extends Animal {
19 constructor(name, species, diet) {
20 super(name); // Wywołanie konstruktora klasy bazowej
21 this.species = species;
22 this.diet = diet;
23 this.isExtinct = false;
24 }
25
26 roar() {
27 const volume = this.diet === "carnivore" ? "głośno" : "cicho";
28 return `${this.name} ryczy ${volume}!`;
29 }
30
31 // Nadpisujemy metodę eat z klasy bazowej
32 eat(food) {
33 if (this.diet === "carnivore" && food === "mięso") {
34 return `${this.name} z apetytem pożera ${food}!`;
35 } else if (this.diet === "herbivore" && food === "rośliny") {
36 return `${this.name} spokojnie skubie ${food}.`;
37 } else {
38 return `${this.name} nie jest zainteresowany jedzeniem ${food}.`;
39 }
40 }
41}
42
43// Klasa pochodna Velociraptor dziedzicząca po Dinosaur
44class Velociraptor extends Dinosaur {
45 constructor(name, speed) {
46 super(name, "Velociraptor", "carnivore");
47 this.speed = speed;
48 this.packHunter = true;
49 }
50
51 hunt() {
52 return `${this.name} poluje w grupie z prędkością ${this.speed} km/h!`;
53 }
54}
55
56// Tworzymy instancje
57const blue = new Velociraptor("Blue", 70);
58
59console.log(blue.eat("mięso")); // "Blue z apetytem pożera mięso!"
60console.log(blue.sleep()); // "Blue śpi."
61console.log(blue.roar()); // "Blue ryczy głośno!"
62console.log(blue.hunt()); // "Blue poluje w grupie z prędkością 70 km/h!"
63
64// Sprawdzamy łańcuch prototypów
65console.log(blue instanceof Velociraptor); // true
66console.log(blue instanceof Dinosaur); // true
67console.log(blue instanceof Animal); // true
68console.log(blue instanceof Object); // trueSkładnia klas w ES6 jest tylko "cukrem składniowym" (syntactic sugar) nad istniejącym mechanizmem prototypów w JavaScript. Pod spodem, JavaScript nadal używa prototypów do implementacji dziedziczenia.
JavaScript nie obsługuje bezpośrednio wielodziedziczenia (dziedziczenia po wielu klasach jednocześnie), ale możemy używać wzorca "mixins" do uzyskania podobnego efektu:
1// Klasa bazowa
2class Dinosaur {
3 constructor(name, species) {
4 this.name = name;
5 this.species = species;
6 }
7
8 roar() {
9 return `${this.name} ryczy!`;
10 }
11}
12
13// Mixin dla zachowań drapieżnych (carnivore behavior)
14const CarnivoreMixin = {
15 hunt(prey) {
16 return `${this.name} poluje na ${prey}!`;
17 },
18
19 bite() {
20 return `${this.name} gryzie z dużą siłą!`;
21 }
22};
23
24// Mixin dla zachowań związanych ze stadnym polowaniem (pack hunting behavior)
25const PackHunterMixin = {
26 coordinateAttack() {
27 return `${this.name} koordynuje atak stada!`;
28 },
29
30 communicateWithPack() {
31 return `${this.name} komunikuje się z innymi członkami stada.`;
32 }
33};
34
35// Mixin dla zachowań związanych z terytorium (territorial behavior)
36const TerritorialMixin = {
37 markTerritory() {
38 return `${this.name} zaznacza swoje terytorium.`;
39 },
40
41 defendTerritory(intruder) {
42 return `${this.name} broni swojego terytorium przed ${intruder}!`;
43 }
44};
45
46// Funkcja do aplikowania mixinów do klasy
47function applyMixins(targetClass, ...mixins) {
48 mixins.forEach(mixin => {
49 Object.getOwnPropertyNames(mixin).forEach(name => {
50 if (name !== 'constructor') {
51 targetClass.prototype[name] = mixin[name];
52 }
53 });
54 });
55}
56
57// Klasa Velociraptor z kilkoma mixinami
58class Velociraptor extends Dinosaur {
59 constructor(name) {
60 super(name, "Velociraptor");
61 this.diet = "carnivore";
62 this.speed = 70; // km/h
63 }
64}
65
66// Aplikujemy mixiny do Velociraptor
67applyMixins(Velociraptor, CarnivoreMixin, PackHunterMixin, TerritorialMixin);
68
69// Tworzymy instancję Velociraptor
70const blue = new Velociraptor("Blue");
71
72// Teraz blue ma dostęp do metod z różnych mixinów
73console.log(blue.roar()); // "Blue ryczy!" (z Dinosaur)
74console.log(blue.hunt("Gallimimus")); // "Blue poluje na Gallimimus!" (z CarnivoreMixin)
75console.log(blue.bite()); // "Blue gryzie z dużą siłą!" (z CarnivoreMixin)
76console.log(blue.coordinateAttack()); // "Blue koordynuje atak stada!" (z PackHunterMixin)
77console.log(blue.defendTerritory("Tyrannosaurus")); // "Blue broni swojego terytorium przed Tyrannosaurus!" (z TerritorialMixin)Mixiny pozwalają na "mieszanie" funkcjonalności z różnych źródeł bez konieczności stosowania wielodziedziczenia, którego JavaScript formalnie nie obsługuje.
Wszystkie obiekty w JavaScript ostatecznie dziedziczą po
Object.prototype. Zawiera on wiele przydatnych metod, takich jak toString(), hasOwnProperty(), valueOf(), itp.1const dino = { name: "Rex" };
2
3// dino dziedziczy metody z Object.prototype
4console.log(dino.toString()); // "[object Object]"
5console.log(dino.hasOwnProperty("name")); // true
6console.log(dino.hasOwnProperty("age")); // false
7
8// Możemy nadpisać metody z Object.prototype
9dino.toString = function() {
10 return `Dinozaur: ${this.name}`;
11};
12
13console.log(dino.toString()); // "Dinozaur: Rex"Poniżej znajduje się przykład bardziej złożonego systemu zarządzania dinozaurami, wykorzystującego dziedziczenie prototypowe:
1// Podstawowa klasa dla wszystkich istot w parku
2class Creature {
3 constructor(name, age) {
4 this.name = name;
5 this.age = age;
6 this.health = 100;
7 this.alive = true;
8 }
9
10 eat(food) {
11 this.health = Math.min(100, this.health + 10);
12 return `${this.name} je ${food}. Zdrowie: ${this.health}%`;
13 }
14
15 sleep() {
16 this.health = Math.min(100, this.health + 5);
17 return `${this.name} śpi. Zdrowie: ${this.health}%`;
18 }
19
20 damage(amount) {
21 this.health = Math.max(0, this.health - amount);
22 if (this.health === 0 && this.alive) {
23 this.alive = false;
24 return `${this.name} nie żyje!`;
25 }
26 return `${this.name} otrzymuje ${amount} obrażeń. Pozostałe zdrowie: ${this.health}%`;
27 }
28}
29
30// Klasa dla dinozaurów, dziedzicząca po Creature
31class Dinosaur extends Creature {
32 constructor(name, species, age, diet) {
33 super(name, age);
34 this.species = species;
35 this.diet = diet;
36 this.containmentLevel = this.calculateContainmentLevel();
37 }
38
39 calculateContainmentLevel() {
40 if (this.diet === "carnivore") {
41 return this.age > 5 ? "Maximum" : "High";
42 } else {
43 return this.age > 10 ? "Medium" : "Standard";
44 }
45 }
46
47 roar() {
48 const volume = this.diet === "carnivore" ? "głośno" : "cicho";
49 return `${this.name} ryczy ${volume}!`;
50 }
51
52 getStatus() {
53 return `
54Dinozaur: ${this.name} (${this.species})
55Wiek: ${this.age} lat
56Dieta: ${this.diet === "carnivore" ? "Mięsożerna" : "Roślinożerna"}
57Zdrowie: ${this.health}%
58Status: ${this.alive ? "Żywy" : "Nieżywy"}
59Poziom bezpieczeństwa: ${this.containmentLevel}
60`;
61 }
62}
63
64// Klasa dla mięsożernych dinozaurów (drapieżników)
65class Carnivore extends Dinosaur {
66 constructor(name, species, age, attackPower) {
67 super(name, species, age, "carnivore");
68 this.attackPower = attackPower;
69 this.huntingSuccess = 0.7; // 70% szans na udane polowanie
70 }
71
72 hunt(prey) {
73 if (!this.alive) return `${this.name} nie żyje, nie może polować.`;
74 if (!prey.alive) return `${prey.name} jest już martwy.`;
75
76 const huntSuccess = Math.random() < this.huntingSuccess;
77
78 if (huntSuccess) {
79 const damage = Math.floor(this.attackPower * (0.7 + Math.random() * 0.6)); // 70-130% mocy ataku
80 const result = prey.damage(damage);
81 this.eat("mięso");
82 return `${this.name} skutecznie poluje na ${prey.name}! ${result}`;
83 } else {
84 return `${this.name} próbuje upolować ${prey.name}, ale nie udaje się.`;
85 }
86 }
87
88 clawAttack(target) {
89 if (!this.alive) return `${this.name} nie żyje, nie może atakować.`;
90 if (!target.alive) return `${target.name} jest już martwy.`;
91
92 const damage = Math.floor(this.attackPower * 0.6);
93 const result = target.damage(damage);
94 return `${this.name} atakuje ${target.name} pazurami! ${result}`;
95 }
96
97 biteAttack(target) {
98 if (!this.alive) return `${this.name} nie żyje, nie może atakować.`;
99 if (!target.alive) return `${target.name} jest już martwy.`;
100
101 const damage = Math.floor(this.attackPower * 1.2);
102 const result = target.damage(damage);
103 return `${this.name} gryzie ${target.name}! ${result}`;
104 }
105}
106
107// Klasa dla roślinożernych dinozaurów
108class Herbivore extends Dinosaur {
109 constructor(name, species, age, defenseRating) {
110 super(name, species, age, "herbivore");
111 this.defenseRating = defenseRating;
112 this.fleeSuccess = 0.6; // 60% szans na udaną ucieczkę
113 }
114
115 grazeVegetation() {
116 if (!this.alive) return `${this.name} nie żyje, nie może jeść.`;
117
118 this.health = Math.min(100, this.health + 15);
119 return `${this.name} skubie rośliny. Zdrowie wzrasta do ${this.health}%.`;
120 }
121
122 flee(predator) {
123 if (!this.alive) return `${this.name} nie żyje, nie może uciekać.`;
124
125 const escapeSuccess = Math.random() < this.fleeSuccess;
126
127 if (escapeSuccess) {
128 this.health = Math.max(0, this.health - 5); // Ucieczka kosztuje energię
129 return `${this.name} skutecznie ucieka przed ${predator.name}!`;
130 } else {
131 return `${this.name} próbuje uciec przed ${predator.name}, ale nie udaje się.`;
132 }
133 }
134
135 // Defensywna metoda - zmniejsza otrzymane obrażenia
136 damage(amount) {
137 // Roślinożercy otrzymują mniej obrażeń dzięki defenseRating
138 const reducedDamage = Math.max(0, amount - this.defenseRating);
139 return super.damage(reducedDamage);
140 }
141}
142
143// Specyficzne gatunki dinozaurów
144class Tyrannosaurus extends Carnivore {
145 constructor(name, age) {
146 super(name, "Tyrannosaurus Rex", age, 35);
147 this.intimidationFactor = 9;
148 this.territorialRange = 5; // km
149 }
150
151 intimidate(target) {
152 return `${this.name} wydaje ogłuszający ryk, który przeraża ${target.name}!`;
153 }
154
155 // Nadpisujemy metodę roar
156 roar() {
157 return `${this.name} wydaje potężny, ogłuszający ryk, który rozchodzi się na kilometry!`;
158 }
159}
160
161class Velociraptor extends Carnivore {
162 constructor(name, age) {
163 super(name, "Velociraptor", age, 20);
164 this.speed = 70; // km/h
165 this.intelligence = 9;
166 this.packHunting = true;
167 }
168
169 coordinateAttack(targets) {
170 if (!Array.isArray(targets) || targets.length < 2) {
171 return `${this.name} potrzebuje co najmniej dwóch celów do koordynacji ataku.`;
172 }
173
174 let results = `${this.name} koordynuje atak stada na ${targets.map(t => t.name).join(', ')}!
175`;
176 targets.forEach(target => {
177 const damage = Math.floor(this.attackPower * 1.5); // Wzmocniony atak stadny
178 results += target.damage(damage) + '
179';
180 });
181
182 return results;
183 }
184
185 ambush(target) {
186 const damage = Math.floor(this.attackPower * 2); // Podwójne obrażenia z zasadzki
187 const result = target.damage(damage);
188 return `${this.name} urządza zasadzkę na ${target.name}! ${result}`;
189 }
190}
191
192class Triceratops extends Herbivore {
193 constructor(name, age) {
194 super(name, "Triceratops", age, 25);
195 this.hornStrength = 30;
196 }
197
198 chargeAttack(target) {
199 const damage = this.hornStrength + Math.floor(Math.random() * 10);
200 const result = target.damage(damage);
201 return `${this.name} szarżuje na ${target.name} swoimi rogami! ${result}`;
202 }
203
204 defendFromAttack(attacker) {
205 const counterDamage = Math.floor(this.hornStrength * 0.5);
206 const result = attacker.damage(counterDamage);
207 return `${this.name} broni się przed ${attacker.name} używając swoich rogów! ${result}`;
208 }
209}
210
211class Brachiosaurus extends Herbivore {
212 constructor(name, age) {
213 super(name, "Brachiosaurus", age, 15);
214 this.height = 13; // metry
215 this.tailLength = 9; // metry
216 }
217
218 reachHighVegetation() {
219 this.health = Math.min(100, this.health + 20);
220 return `${this.name} sięga swoją długą szyją po liście wysokich drzew. Zdrowie wzrasta do ${this.health}%.`;
221 }
222
223 tailWhip(target) {
224 const damage = Math.floor(this.tailLength * 2);
225 const result = target.damage(damage);
226 return `${this.name} uderza ${target.name} swoim ogromnym ogonem! ${result}`;
227 }
228}
229
230// Symulacja parku jurajskiego
231function runJurassicParkSimulation() {
232 console.log("=== SYMULACJA PARKU JURAJSKIEGO ===
233");
234
235 // Tworzenie dinozaurów
236 const rex = new Tyrannosaurus("Rex", 8);
237 const blue = new Velociraptor("Blue", 4);
238 const delta = new Velociraptor("Delta", 4);
239 const trixie = new Triceratops("Trixie", 12);
240 const bronty = new Brachiosaurus("Bronty", 25);
241
242 // Wyświetlanie informacji o dinozaurach
243 console.log(rex.getStatus());
244 console.log(blue.getStatus());
245 console.log(trixie.getStatus());
246 console.log(bronty.getStatus());
247
248 console.log("
249=== INTERAKCJE DINOZAURÓW ===
250");
251
252 // Interakcje między dinozaurami
253 console.log(rex.roar());
254 console.log(blue.roar());
255 console.log(trixie.grazeVegetation());
256 console.log(bronty.reachHighVegetation());
257
258 console.log("
259=== POJEDYNEK: REX VS TRIXIE ===
260");
261
262 console.log(rex.intimidate(trixie));
263 console.log(trixie.flee(rex));
264 console.log(rex.hunt(trixie));
265 console.log(trixie.defendFromAttack(rex));
266 console.log(rex.biteAttack(trixie));
267
268 console.log("
269=== POLOWANIE STADA VELOCIRAPTORÓW ===
270");
271
272 console.log(blue.coordinateAttack([trixie, bronty]));
273 console.log(delta.ambush(bronty));
274 console.log(bronty.tailWhip(delta));
275
276 console.log("
277=== STAN DINOZAURÓW PO INTERAKCJACH ===
278");
279
280 console.log(rex.getStatus());
281 console.log(blue.getStatus());
282 console.log(delta.getStatus());
283 console.log(trixie.getStatus());
284 console.log(bronty.getStatus());
285}
286
287// Uruchomienie symulacji
288runJurassicParkSimulation();Dziedziczenie prototypowe w JavaScript różni się od klasycznego dziedziczenia w innych językach obiektowych:
Podobnie jak Park Jurajski eksperymentował z różnymi metodami klonowania dinozaurów, świat JavaScript również ewoluuje, oferując nowe środowiska uruchomieniowe dla kodu po stronie serwera. Oprócz Node.js, który przez lata był standardem de facto, pojawiły się nowe, ciekawe alternatywy: Deno i Bun.
Deno został stworzony przez Ryana Dahla - tego samego programistę, który stworzył Node.js! W 2018 roku, podczas prezentacji zatytułowanej "10 rzeczy, których żałuję w Node.js", Ryan przedstawił swoją wizję lepszego środowiska uruchomieniowego dla JavaScript i TypeScript. To jak dr Hammond w Parku Jurajskim, który po pierwszych niepowodzeniach próbował stworzyć doskonalsze, bezpieczniejsze rozwiązanie.
1. TypeScript natywnie
Deno obsługuje TypeScript "out of the box" - nie potrzebujesz dodatkowej konfiguracji ani transpilacji:
1// dinosaur.ts
2interface Dinosaur {
3 name: string;
4 species: string;
5 age: number;
6 dangerous: boolean;
7}
8
9function createDinosaur(name: string, species: string): Dinosaur {
10 return {
11 name,
12 species,
13 age: 0,
14 dangerous: species === "Tyrannosaurus Rex"
15 };
16}
17
18const rex = createDinosaur("Rexy", "Tyrannosaurus Rex");
19console.log(rex);Uruchamiasz to bezpośrednio:
1deno run dinosaur.ts2. System uprawnień (Permissions)
Największą innowacją Deno jest system uprawnień. W Node.js każdy skrypt ma pełny dostęp do systemu plików, sieci i zmiennych środowiskowych. Deno wymaga jawnego nadania uprawnień - to jak systemy bezpieczeństwa w Parku Jurajskim, które kontrolują dostęp do różnych stref:
1# Bez uprawnień - skrypt nie może nic zrobić
2deno run script.ts
3
4# Z dostępem do sieci
5deno run --allow-net script.ts
6
7# Z dostępem do plików w określonym katalogu
8deno run --allow-read=/data --allow-write=/data script.ts
9
10# Z dostępem do zmiennych środowiskowych
11deno run --allow-env script.ts
12
13# Wszystkie uprawnienia (używaj ostrożnie!)
14deno run --allow-all script.tsPrzykład z systemem zabezpieczeń parku:
1// security-monitor.ts
2// Ten skrypt monitoruje stan zabezpieczeń i zapisuje logi
3
4// Próba odczytu czujników bez uprawnień
5try {
6 const sensors = await fetch("http://park-sensors.internal/status");
7 const data = await sensors.json();
8 console.log("Status czujników:", data);
9} catch (error) {
10 console.error("Brak uprawnień do sieci:", error.message);
11}
12
13// Próba zapisu logów bez uprawnień
14try {
15 await Deno.writeTextFile("./logs/security.log", "System sprawny
16");
17} catch (error) {
18 console.error("Brak uprawnień do zapisu:", error.message);
19}Uruchomienie:
1# To się nie powiedzie - brak uprawnień
2deno run security-monitor.ts
3
4# To zadziała
5deno run --allow-net --allow-write=./logs security-monitor.ts3. Brak node_modules
Deno nie używa
node_modules. Zamiast tego importuje moduły bezpośrednio z URL-i i cache'uje je lokalnie:1// Import z URL
2import { serve } from "https://deno.land/std@0.200.0/http/server.ts";
3
4// Serwer HTTP obsługujący zapytania o dinozaury
5const handler = (req: Request): Response => {
6 const dinosaurs = [
7 { name: "Rexy", species: "T-Rex" },
8 { name: "Blue", species: "Velociraptor" }
9 ];
10
11 return new Response(JSON.stringify(dinosaurs), {
12 headers: { "content-type": "application/json" }
13 });
14};
15
16serve(handler, { port: 8000 });4. Standardowa biblioteka
Deno ma dobrze zaprojektowaną standardową bibliotekę, podobną do Go czy Pythona. Nie musisz instalować dziesiątek pakietów dla podstawowych operacji:
1// Praca z plikami
2import { readLines } from "https://deno.land/std@0.200.0/io/mod.ts";
3
4// Czytanie logów dinozaurów linia po linii
5const file = await Deno.open("dinosaur-logs.txt");
6for await (const line of readLines(file)) {
7 console.log("Log entry:", line);
8}
9file.close();
10
11// Praca z UUID
12import { v4 } from "https://deno.land/std@0.200.0/uuid/mod.ts";
13
14const dinosaurId = v4.generate();
15console.log("Nowy ID dinozaura:", dinosaurId);5. Wbudowane narzędzia
Deno ma wbudowane narzędzia, które w Node.js wymagałyby dodatkowych pakietów:
1# Formatowanie kodu
2deno fmt script.ts
3
4# Linting
5deno lint script.ts
6
7# Testy
8deno test tests/
9
10# Bundling
11deno bundle main.ts output.js
12
13# Dokumentacja
14deno doc module.tsUżywaj Deno, gdy:
Zostań przy Node.js, gdy:
Bun to kolejna nowoczesna alternatywa, która skupia się na wydajności. Napisana w Zig zamiast C++ (jak Node.js i Deno), oferuje imponującą szybkość:
1// Bun obsługuje TypeScript natywnie
2import { serve } from "bun";
3
4serve({
5 port: 3000,
6 fetch(req) {
7 return new Response("Witaj w systemie zarządzania Parkiem Jurajskim!");
8 }
9});1. Błyskawiczna szybkość
2. Kompatybilność z Node.js
3. Wbudowane narzędzia
1# Bundler
2bun build ./index.ts --outdir ./dist
3
4# Test runner
5bun test
6
7# Package manager (super szybki!)
8bun install
9bun add lodash4. Wbudowany SQLite
1import { Database } from "bun:sqlite";
2
3const db = new Database("dinosaurs.db");
4
5// Tworzenie tabeli
6db.run(`
7 CREATE TABLE IF NOT EXISTS dinosaurs (
8 id INTEGER PRIMARY KEY,
9 name TEXT,
10 species TEXT,
11 age INTEGER
12 )
13`);
14
15// Wstawianie danych
16const insert = db.prepare("INSERT INTO dinosaurs (name, species, age) VALUES (?, ?, ?)");
17insert.run("Rexy", "T-Rex", 8);
18insert.run("Blue", "Velociraptor", 4);
19
20// Zapytania
21const dinosaurs = db.query("SELECT * FROM dinosaurs").all();
22console.log(dinosaurs);| Cecha | Node.js | Deno | Bun | |-------|---------|------|-----| | TypeScript | Wymaga konfiguracji | Natywnie | Natywnie | | Package manager | npm/yarn/pnpm | Wbudowany | Super szybki | | System uprawnień | Brak | Tak | Brak | | Kompatybilność npm | 100% | ~Częściowa | Wysoka | | Wydajność | Standardowa | Dobra | Doskonała | | Ekosystem | Ogromny | Rosnący | Młody | | Stabilność | Produkcyjna | Stabilna | Beta/Rozwój |
Node.js + Express:
1// Wymaga: npm install express
2const express = require('express');
3const app = express();
4
5app.get('/dinosaurs', (req, res) => {
6 res.json([
7 { name: "Rexy", species: "T-Rex" },
8 { name: "Blue", species: "Velociraptor" }
9 ]);
10});
11
12app.listen(3000, () => console.log('Server running on port 3000'));Deno:
1// Brak instalacji - wszystko z URL
2import { serve } from "https://deno.land/std@0.200.0/http/server.ts";
3
4serve((req) => {
5 const dinosaurs = [
6 { name: "Rexy", species: "T-Rex" },
7 { name: "Blue", species: "Velociraptor" }
8 ];
9
10 return new Response(JSON.stringify(dinosaurs), {
11 headers: { "content-type": "application/json" }
12 });
13}, { port: 3000 });
14
15console.log("Server running on port 3000");Bun:
1// Wbudowane API, super szybkie
2Bun.serve({
3 port: 3000,
4 fetch(req) {
5 const dinosaurs = [
6 { name: "Rexy", species: "T-Rex" },
7 { name: "Blue", species: "Velociraptor" }
8 ];
9
10 return new Response(JSON.stringify(dinosaurs), {
11 headers: { "content-type": "application/json" }
12 });
13 }
14});
15
16console.log("Server running on port 3000");Podobnie jak Park Jurajski eksperymentował z różnymi metodami, aby znaleźć najlepsze rozwiązania, świat JavaScript oferuje różne środowiska uruchomieniowe, każde z własnymi zaletami:
Wybór zależy od twoich potrzeb, ale znajomość alternatyw sprawia, że jesteś lepiej przygotowany na przyszłość JavaScript!
Prototypy i dziedziczenie prototypowe są fundamentalnymi koncepcjami w JavaScript. Nawet gdy używamy nowoczesnej składni klas (ES6+), ważne jest zrozumienie leżącego u podstaw mechanizmu prototypów, aby w pełni wykorzystać możliwości języka. W naszym Parku Jurajskim dziedziczenie prototypowe pomaga nam modelować różne typy dinozaurów i ich zachowań w spójny, wydajny sposób.
Główne koncepcje, które omawialiśmy, to:
Zrozumienie prototypów i dziedziczenia w JavaScript jest kluczem do pisania efektywnego, dobrze zorganizowanego kodu, który może modelować złożone relacje między obiektami, takie jak te w naszym wirtualnym Parku Jurajskim.