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