Wracamy do programu
tworzącego Winstonów.
Dodałam nowy typ obiektu: Hopper,
bo Hopper czuła się pominięta.
Definiuję ją tak samo jak Winstona.
Zaczynam od konstruktora,
potem są właściwości,
rysowanie i mówienie...
Dodałam też metodę „Hura!”,
bo Hopper bardzo lubi świętować.
A Winston - wcale.
U dołu funkcji stworzyłam
dwa nowe obiekty „Hopper”:
Dużą i Małą. Narysowałam je.
Jednej przypisałam funkcję
mówienia, drugiej - „Hura”.
I dobrze. Patrząc
w ten fragment programu,
możecie zauważyć coś ciekawego.
Program dla Hopper
jest podobny do tego dla Winstona.
Zwróćcie uwagę na konstruktora.
Może pamiętacie... To dokładnie
ten sam program, co u Winstona.
I funkcja „talk” jest identyczna
jak u niego.
Jedno i drugie ma funkcję „draw”.
Te dwa typy obiektów wiele łączy!
To ma sens, bo Hopper i Winston
są podobnymi typami w naszym świecie.
Zresztą w prawdziwym świecie,
poza komputerem,
wiele typów obiektów
przypomina inne.
Np. w królestwie zwierząt.
Wszystkie zwierzęta
są do siebie podobne.
Ale są różne typy zwierząt,
np. ludzie.
Ludzie są podobni do zwierząt,
a także do siebie nawzajem.
Możemy więc powiedzieć,
że „zwierzę” to typ obiektu,
od którego typ obiektu „człowiek”
dziedziczy funkcjonalność.
Nie zaczynamy się z niczego;
dodajemy cechy do tych,
które mamy jako zwierzęta.
Wszystkie zwierzęta wydają odgłosy,
a ludzie dodatkowo mówią.
Pojęcie dziedziczenia obiektów
przydaje się też w programowaniu.
W JavaScript możemy stworzyć
łańcuch dziedziczenia obiektów.
Zastanówmy się, co łączy
nasze typy obiektów.
I wymyślmy nazwę.
Stworzymy nowy typ obiektu,
reprezentujący obiekt bazowy.
Dajmy nazwę: „Creatures”
(istoty). Bo nimi są.
Piszemy: „var creature =”...
Potrzebujemy konstruktora.
Ma go Hopper.
Taki sam jest u Winstona.
W porządku? I teraz...
Zobaczmy... Teraz chcemy...
Co dalej? Może dodamy
funkcję „talk”?
Zabierzemy ją.
Musi być w prototypie „creature”.
W porządku.
Mamy już typ obiektu „creature”.
Ale Hopper musi się dowiedzieć,
że ma na opierać swoje działanie
na „creature”.
Załatwimy to, pisząc ten wiersz.
Piszemy: „Hopper.prototype”
równa się „object.create”
„creature.prototype”.
Ten wiersz każe językowi JavaScript
oprzeć prototyp,
czyli funkcjonalność Hopper,
na prototypie „creature”.
Zawsze, gdy program szuka
funkcji u Hopper,
najpierw spojrzy na prototyp.
A jeśli tam nie znajdzie,
to spojrzy na prototyp „creature”.
To nazywamy łańcuchem prototypów.
Po zrobieniu tego
powinno być możliwe skasowanie
funkcji „talk” u Hopper.
Bo jest już w „creature”.
Wyżej w łańcuchu prototypów.
Spróbujmy!
Gotowi? Udało się!
Bo program znalazł funkcję
w prototypie „creature”.
Spróbujmy skasować
tę funkcję u Winstona.
Nie udało się. Obiekt nie ma
metody „talk”.
A dlaczego?
Są konstruktor i „draw”,
a „talk” usunęliśmy.
Zapomnieliśmy powiedzieć
prototypowi Winstona,
że ma się opierać na prototypie
„creature”. Ważny wiersz:
„Winston.prototype=object.create
creature.portotype”.
Już! Zauważcie coś istotnego:
Ten wiersz jest po konstruktorze,
ale wpisuję go, zanim dodam coś
do prototypu Winstona.
Zazwyczaj chcemy powiedzieć,
że na tym będzie się opierać
początkowy prototyp.
Możemy dodawać do prototypu
kolejne elementy.
Winston i Hopper mogą mieć
charakterystyczne cechy,
których nie ma w „creatures”.
Super, że można je zdefiniować!
No dobrze. Widzimy
powtarzające się fragmenty.
To konstruktor.
Ten sam we wszystkich
trzech przypadkach.
Czy możemy go skasować?
Spróbujmy.
Hmmm... Nie udało się.
Hopper pokazuje się w lewym górnym
rogu. Nie pamięta nic o sobie.
Bo JavaScript nie zakłada,
że chcemy tego samego konstruktora,
nawet gdy ma być w prototypie.
Pozwala nam zdefiniować
konstruktora dla tych obiektów.
I daje łatwy sposób,
by przywołać
tę funkcję z obiektu.
Piszemy więc: „creature”...
kropka, „call”,
„this,nickname,age,x,y”.
Dobrze.
Program robi to
(zauważcie - zadziałało!)...
przywołuje funkcję
„creature”, konstruktora.
Przywołuje funkcję, ale mówi:
„przywołaj tę funkcję tak
jak z obiektu Hopper,
jak z tymi argumentami”.
Tak została przywołana Hopper.
Program zostanie wykonany tak,
jakby był tutaj.
I o to właśnie nam chodzi.
Udało się!
Możemy skopiować ten wiersz
także do konstruktora Winstona.
To działa. Tak!
Dobrze. Patrzcie:
zamknęliśmy wspólne właściwości
i funkcjonalność
w jednym typie obiektu bazowego,
„creature”,
i na tej podstawie stworzyliśmy
dwa typy obiektów.
Dziedziczą funkcjonalność,
ale dodają też własną.
Wystarczy zmienić tę funkcjonalność
w jednym miejscu.
Np. moglibyśmy zmienić wiek,
pisząc: „plus lata”.
Teraz każdy ma na końcu „lata”.
Możemy też zmienić
funkcje „talk”. „Super!”.
Teraz każdy Winston
i Hopper mówią: „Super!”.
Wiecie, jak tworzyć typy obiektów
i jak przebiega dziedziczenie.
Pomyślcie, jak to wykorzystać
w rysowaniu, animacjach,
symulacjach i grach.
Np. macie grę
z wieloma postaciami...
Wszystkie biegają,
ale tylko niektóre skaczą.
Tu trzeba wykorzystać typy
obiektów i dziedziczenie!
Na pewno wymyślicie też
mnóstwo własnych zastosowań.