w Praktyce
9 godz. 47 min · JavaScript · Full-stack i Programowanie
Piotr PalarzWeb DeveloperW pierwszym rozdziale tego kursu poznasz bardzo dokładnie koncepcje programowania zorientowanego obiektowo. W jednej z lekcji, omówimy wszystkie z nich na przykładzie pseudo-kodu, niezwiązanego bezpośrednio z językiem JavaScript. Dzięki temu zrozumiesz, jak realizowane jest programowanie obiektowe w wielu popularnych językach programowania. Dowiesz się czym jest klasa, obiekt, instancja, właściwości, metody, a także zrozumiesz takie pojęcia jak dziedziczenie, polimorfizm, abstrakcja czy enkapsulacja. Następnie przejdziemy już do języka JavaScript i do zrozumienia tego, w jaki sposób powyższe koncepcje są w nim zaimplementowane. Język ten nie jest bowiem w 100% klasycznie obiektowy, jak np. język PHP. Co jednak bardzo istotne, JavaScript implementuje tzw. dziedziczenie prototypowe, którego bardzo dogłębne zrozumienie, gwarantuje zrozumienie całego języka JavaScript. Dowiesz się zatem czym są konstruktory i jak je tworzyć, a także jak korzystać z prototypu funkcji. Dzięki temu będziemy mogli wykorzystać obiektowość do własnych celów, co później w praktycznych przykładach uczynimy.Na przestrzeni kilku lekcji zobaczysz krok po kroku, jak w języku JavaScript zaimplementować dziedziczenie wielokrotne. Taka wiedza absolutnie otwiera oczy na zależności rządzące obiektami w tym języku. Dowiesz się również jak rozszerzać wbudowane konstrukcje języka JavaScript czy Obiektowego Modelu Dokumentu, a także kiedy powinniśmy to robić. Omówimy problemy, jakie mogą się pojawiać przy korzystaniu ze słowa kluczowego this, a także sposoby ich rozwiązania. Poznasz 3 bardzo ważne metody funkcji: call, apply i bind.JavaScript ma wiele specyficznych cech, a jedną z nich są tzw. domknięcia. W jednym z rozdziałów zobaczysz, do czego one służą i kiedy powinniśmy z nich korzystać. Dopełnimy tym również wiedzę związaną z programowaniem obiektowym i tzw. prywatnością.Rozdział o obiektowości został celowo umieszczony na samym początku kursu, gdyż zrozumienie tych koncepcji pozwala patrzeć zupełnie inaczej na wszystkie pozostałe. Po tym rozdziale przejdziemy do omówienia bardzo popularnego, lekkiego formatu wymiany danych JSON. Dowiesz się jak wygląda składnia tego formatu, a także jak współpracuje on z językiem JavaScript.Następnie przejdziemy do bardzo ekscytującego tematu, jakim jest technologia AJAX. Dzięki niej możemy wysyłać zapytania do serwera i pobierać dane, bez przeładowywania witryny. Do zrozumienia AJAXa niezbędne jest jednak rozumienie działania protokołu HTTP, na którym opiera się sieć www. Z tego powodu, jedna z lekcji została w 100% poświęcona HTTP i dowiesz się z niej jak wysyłane są żądania i odpowiedzi, a także tzw. nagłówki, które będą dla nas niezwykle istotne. Chwilę później omówimy również bardzo ważne mechanizmy: Same- origin policy i CORS, które blokują możliwość wysyłania żądań AJAX do pewnych serwerów. Kiedy już będziesz rozumiał te niezwykle istotne koncepcje, przejdziemy do napisania pierwszego kodu, wysyłającego asynchroniczne żądanie do serwera. Następnie dowiesz się jak wysyłać, a także odbierać dane z serwera, umieszczać je na stronie oraz śledzić postęp zarówno wysyłania, jak i pobierania danych.W tym rozdziale na przestrzeni kilku lekcji napiszemy swoją własną bibliotekę AJAX, która pozwoli w niezwykle łatwy sposób wysyłać i odbierać dane. Skorzystamy w niej już w kolejnej lekcji, gdzie zobaczysz, jak wysłać wiadomość e-mail z użyciem AJAX i PHP po stronie serwera. Gotowy skrypt, będziesz mógł umieścić na własnej stronie internetowej. Na zakończenie tego rozdziału omówimy również dokładnie niezwykle sprytną technikę o nazwie JSONP, które pozwala wysyłać tzw. żądania “cross-origin”. Przekonasz się dlaczego taka technika powstała i jak współpraca klienta i serwera potrafi obejść zabezpieczenia przeglądarek.Kolejnym tematem omówionym w kursie będą Cookies, czyli tzw. ciasteczka. Dowiesz się czym są, w jakim celu istnieją, a także jak są ustawianie przez serwer. Co ciekawe, również z poziomu języka JavaScript będziemy mieli do nich dostęp, a zatem zobaczysz jak je ustawiać i odczytywać. Na tym etapie przyda nam się wiedza zdobyta w lekcji o protokole HTTP.W kursie znajdziesz również rozdział poświęcony w 100% wyrażeniom regularnym. To mechanizm języka, który pozwala dopasowywać tekst do definiowanych przez nas wzorców. Wyrażenia regularne potrafią być bardzo zaawansowane i odstraszać, dlatego rozłożymy je na czynniki pierwsze, byś dokładnie je zrozumiał. Na przestrzeni wielu lekcji dowiesz się jakie są specjalne symbole wykorzystywane przy wyrażeniach regularnych, czym są kwantyfikatory, kotwice czy np. grupowanie. Następnie przejdziemy do wykorzystania wyrażeń regularnych w języku JavaScript. Poznasz zarówno metody obiektu RegExp jak i te dostępne na obiekcie String. Dzięki nim będziemy mogli wyszukiwać np. adresów e- mail w podanym tekście, usuwać je lub zamieniać na coś innego. Jako praktyczny projekt, stworzymy “Antybluzgacz”. Wykorzystamy do tego celu programowanie obiektowe, a sama aplikacja będzie pozwalała filtrować wpisywany przez użytkownika tekst wiadomości i wyłapywać w nim niecenzuralne słowa. Wszystkie z nich zamienimy na przypadkową reprezentację znaków, np. $#!@%^. Ten projekt pokaże Ci nie tylko jak korzystać z wyrażeń regularnych, ale także jak profesjonalnie pracować z kodem JavaScript.W przedostatnim rozdziale tego kursu znajdziesz lekcje związane z dobrymi praktykami pracy. Omówimy tzw. “strict mode” języka JavaScript, zagadnienia związane z minifikacją i konkatenacją skryptów, sprawdzaniem poprawności pisanego kodu oraz delegacją zdarzeń DOM.Na końcu kursu znajdziesz 10 krótkich lekcji z serii Tips & Tricks, które przybliżą Ci wiele ciekawych konstrukcji języka JavaScript, a także technik na rozwiązanie różnych problemów. Zobaczysz np. jak wczytać obrazy w tle czy jak wyświetlić na pasku tytułowym witryny migające powiadomienie.Podsumowując, kurs ten jest dopełnieniem wiedzy z zakresu języka JavaScript i Obiektowego Modelu Dokumentu z kursu “JavaScript od Podstaw”. Poprzedni kurs nie jest wymagany, lecz jego przerobienie z pewnością pozwoli dużo lepiej przyswoić wiedzę z tego kursu, gdyż były one przygotowywane równocześnie. Oba te materiały, pozwolą od zera, wskoczyć na naprawdę wysoki poziom rozumienia języka, a także dobrych praktyk pisania kodu.
Do tego kursu mogą jednak przystąpić wszystkie osoby, które znają już podstawy języka HTML, CSS i JavaScript i chcą poszerzyć swoją wiedzę, a także spojrzeć zupełnie inaczej na ten język.
W tej lekcji pokażę ci już na żywo, jak działa dziedziczenie prototypowe w języku JavaScript, czyli to,
co pokazałem w lekcji poprzedniej tutaj sobie zmaterializujemy plus pokażę ci jeszcze kilka innych rzeczy.
Otóż przejdźmy sobie od razu do scripts.js.
Mam tutaj przykład taki jak w prezentacji, czyli utworzyłem nową tablicę i następnie w konsoli chcę
wyświetlić to, co zwróci metoda arr.indexOf. Podałem jedynkę, czyli w tej tablicy chcę znaleźć sobie jedynkę.
Jeżeli jest, proszę zwróć mi indeks, pod którym się znajduje.
Tutaj jest na samym początku, czyli indeks zero. Odświeżam to w przeglądarce i mamy indeks zero.
Nic skomplikowanego.
Chociaż jak wspominałem w prezentacji, bezpośrednio na tym obiekcie nie mamy takiej metody jak indexOf
i gdybym tutaj skorzystał z console.dir albo w skrócie można napisać dir i odwołał się do
naszego Array, byśmy tam zajrzeli, to mamy tutaj coś takiego jak zero, czyli pierwszy indeks, tutaj
jeden czyli drugi indeks oraz length. Czyli bezpośrednio na tym obiekcie nie ma czegoś takiego jak indexOf,
bo to zostało odziedziczone z prototypu, o czym jeszcze powiem za moment.
Natomiast co stanie się, jeżeli teraz napiszę array, czyli odwołam się do tego obiektu, indexOf równa się
funkcja i chciałbym, żeby ona zawsze zwracała false.
Nie przyjmuje żadnego parametru, więc jeśli go podamy, to nie ma żadnego błędu.
Teraz znowu chcę się odwołać do arr.indexOf. Sprawdźmy, co zostanie nam zwrócone.
Tym razem jest to false.
I teraz gdybyśmy sobie zajrzeli - dir i następnie arr, to okaże się, że tutaj wewnątrz mamy 0,1 oraz funkcję indexOf,
którą przypisałem. A więc ta funkcja jest przypisana bezpośrednio do tego obiektu.
I teraz za każdym razem będzie nam zwracała false.
To oznacza tylko tyle że, jak wspominałem w prezentacji, wyszukiwania jest najkrótsza możliwa droga, aby
znaleźć dla nas funkcję. Kiedy odwołałem się arr.indexOf w tym miejscu, zostało sprawdzone, czy obiekt
array, czyli ten, zawiera taką metodę. Zawiera, bo ją tutaj przypisałem, więc zwróci nam jej wartość. Jeżeli jej
nie ma, to dopiero wtedy odwołujemy się do prototypu,
co za moment pokażę.
Natomiast jeżeli tutaj pod spodem utworzę sobie teraz nową tablicę, w ten sposób, i odwołam się do jej arr.indexOf(5),
zobaczmy, to zostanie nam zwrócone 1, poprawnie.
W tym przypadku po odwołaniu się do indexOf znowu sprawdzamy najkrótszą możliwą drogę.
Czy tutaj mamy indexOf?
Nie ma, bo ja tu przypisałem
owszem, ale konkretnie do tego jednego obiektu.
Tylko do tego obiektu. Czyli tutaj został sprawdzony prototyp i gdybym tutaj napisał dir(arr2) i zajrzelibyśmy sobie
do właśnie tej tablicy, która jest, jak wiesz, w JavaScript również obiektem, to mamy 0, 1 oraz length.
Nie mamy tutaj nic więcej. A jeżeli znowu napiszemy dir(arr), czyli do tej pierwszej tablicy, to tutaj ta metoda
indexOf się znajduje.
Wspominałem w prezentacji, że taka metoda indexOf znajduje się w Array.prototype, więc chcę ci to teraz
pokazać.
Możemy w konsoli wpisać Array. Zanim to zrobię, to też pokażę ci, że samo Array poprzez typeOf
jest to funkcja.
Czyli możemy ją wywoływać poprzez Array, w ten sposób. Ja w pierwszej części tego kursu pokazywałem, że np. string
i inne konstruktory możemy wywoływać z new, niektóre bez new i działają tak samo albo różnie - czy użyjemy
tutaj new czy nie użyjemy. Natomiast wiesz, że tablicę możemy stworzyć właśnie poprzez new.
I ważne jest, że new używamy tylko z funkcjami.
Dlatego właśnie pokazałem ci, że typeOf Array jest to funkcja. I każda funkcja, co mówiłem w prezentacji w poprzedniej
lekcji, zawiera coś takiego jak prototype, czyli jej prototyp. Jeżeli tutaj zajrzymy, w konsoli została nam wyświetlona
tylko taka tablica, bo tutaj zostało użyte console.log, więc skorzystajmy z console.dir i teraz możemy zajrzeć
do środka.
Jeżeli zajrzymy do Array.prototype, to okaże się, że jest tutaj bardzo wiele metod, które my znamy,
i z których możemy korzystać na różnych tablicach.
I teraz poszukajmy metody IndexOf.
Jak widzisz, ona tutaj się znajduje. Czyli w momencie kiedy korzystaliśmy na tej drugiej tablicy z indexOf,
nie została znaleziona tutaj, ale na Array.prototype została znaleziona i została dla nas użyta. I to oznacza
tylko tyle, że kiedy tworzymy nową tablicę, to każda z tych tablic nie jest od razu inicjalizowana
z nową funkcją, bo byłoby to bez sensu.
Czyli tak jak pokazałem ci dir(arr), zapisaliśmy sobie tutaj funkcję indexOf. Czyli teraz gdyby tworząc każdą
tablicę, była w niej taka funkcja, to zajmowałoby to dużo pamięci zupełnie niepotrzebnie, bo funkcja
byłaby w każdej tablicy. A tak taka funkcja znajduje się w prototypie, tylko w jednym miejscu,
kiedy znowu przejdziemy do Array.prototype i wszystkie tablice mogą dzięki temu z tego korzystać. To jest
bardzo ważne.
Natomiast teraz kolejna bardzo ważna sprawa -
skąd my wiemy, że jeżeli korzystamy z indexOf, to najpierw będzie przeszukiwany Array.prototype. To znaczy najpierw
będzie przeszukiwany
ten obiekt, tak jak wspomniałem, ale jeżeli tu nie ma takiej metody, to Array.prototype.
I dlaczego właśnie Array.prototype, a nie coś innego?
Jak sprawdzić, jaki obiekt będzie przeszukiwany?
No i możemy to zrobić w bardzo łatwy sposób.
Ja tutaj odświeżę. Mamy arr, czyli naszą tablicę utworzoną u góry, i na niej możemy odwołać się do czegoś
takiego jak constructor. To nam wskaże, z jakiego konstruktora taki obiekt został utworzony.
Nawet jeżeli nie utworzyliśmy go poprzez new Array, bo przecież tego nie zrobiłem. Czyli napiszmy arr.constructor.
Zostanie nam zwrócona referencja do funkcji Array.
Przed momentem ci pokazałem, że Array właśnie to jest funkcja, czyli arr.constructor nam kieruje na taką funkcję.
Czyli array.constructor to jest dokładnie to samo, co Array. Gdybyśmy tutaj napisali array.constructor
i chcieli porównać, czy to jest dokładnie ta sama funkcja.
Okazuje się, że tak.
Czyli kiedy odwoływałem się do Array.prototype, to jest to samo,
gdybym się odwołał do array, czyli do konkretnej już instancji utworzonej tutaj, .constructor,
.prototype. Zerknijmy sobie w dir.
Zobaczmy.
I tutaj będą te same metody, czyli arr.constructor kieruje nam na Array, a .prototype kieruje na ten obiekt,
tak samo jak Array.prototype.
W ten sposób możemy to sprawdzić. Gdybyśmy mieli obiekt, utworzymy sobie tu nowy obiekt. Musimy go opleść
takimi nawiasami, żeby nie było błędu. Czyli zwykły obiekt utworzyłem, bez new Object.
Możemy go utworzyć w ten sposób i tak to zazwyczaj robimy.
Jeśli tutaj się odwołam do constructor - w ten sposób - to zostanie nam zwrócony Object.
Czyli jeżeli na tak zwykłym obiekcie będziemy się odwoływać do jakichś metod, to wiemy, że one nie
będą szukane gdzieś Array.prototype czy w innych miejscach, ale od razu w Object.prototype, bo
to zostało nam tutaj zwrócone.
Natomiast to jest bardzo ciekawe.
Wróćmy sobie jeszcze do arr.constructor, to znowu jeżeli ja wyświetlę dir(arr), to przecież nie ma tutaj
czegoś takiego jak constructor. Mamy 0, 1,
indexOf, którą ja przypisałem tutaj ręcznie, w ten sposób. Ale constructora nie ma. Więc jak to się dzieje,
że mogę napisać arr.constructor i to zostaje znalezione?Znowu tutaj korzystamy z dziedziczenia prototypowego.
Czyli kiedy ja napisałem w tym miejscu
arr.constructor, to zostało sprawdzone, czy constructor znajduje się na tym obiekcie array, czyli tutaj.
Gdybym ja tutaj napisał arr.constructor i coś przypisał, to zostałoby zwrócone.
Ale skoro bezpośrednio na tym obiekcie nie ma takiej właściwości jak constructor, to przechodzimy dalej,
czyli do arr.constructor i tam tego szukamy.
No i teraz przewrotność losu jest taka, że jeżeli zajrzymy sobie do Array.prototype, to okaże się, że wewnątrz mamy
coś takiego jak constructor. Pokazywałem ci w prezentacji, że każda funkcja ma prototyp i jeżeli nawet tworzymy
nową funkcję i przejdziemy do jej prototypu, to będzie on miał jedno pole i będzie to pole constructor,
które kieruje na tę właśnie funkcję.
Czyli to jest właśnie ciekawe, że jeżeli tutaj się odwołuje do arr.constructor, to jest on wyszukiwany najpierw
w tablicy, jego tam nie ma,
więc przechodzimy do prototypu i tam go znajdujemy.
Mam nadzieję, że do tej pory nadążasz.
Pokazałem ci, że jeżeli wyszukujemy jakiejś metody, czyli chcemy jej użyć na obiekcie, to jest sprawdzane,
czy na tym obiekcie ona jest.
Jeżeli nie, przechodzimy do jego prototypu, który gdybyśmy chcieli sprawdzić, dlaczego przy tablicach jest
to array, możemy poprzez
array.constructor.
Pamiętaj jednak, że array.constructor tak jak tutaj zrobiłem, możemy wywołać już na konkretnym
utworzonym obiekcie, a nie na tym konstruktorzy Array, bo gdybyśmy tu napisali constructor, to zostałoby
nam zwrócone undefined.
Teraz chcę ci pokazać kolejną bardzo ważną rzecz, tutaj może odświeżymy.
Otóż pokazywałem w prezentacji, że możemy odwołać się do metody valueOf i zróbmy to np. na tej tablicy.
ValueOf zwraca nam w przypadku obiektów
właśnie ten obiekt i jego reprezentacje, czyli w przypadku tablicy arr2.valueOf będzie dokładnie to
samo, co gdybyśmy napisali tylko arr2.
Niemniej jednak po odświeżeniu ta tablica jest zwracana i nam to działa.
Ale jeżeli sobie znowu zrobimy dir(Array.prototype) i zajrzymy do środka, to okaże się, że takiej metody jak valueOf
nie ma.
Prześledzimy sobie raz jeszcze drogę. Napisałem arr.valueOf, sprawdziliśmy, czy tutaj na tym obiekcie bezpośrednio
jest valueOf.
Nie ma.
Przeszliśmy do Array.prototype sprawdzić, czy tam jest.
Nie ma tego tam.
Więc dlaczego teraz przeszliśmy dalej i to znaleźliśmy?
Otóż zauważ jedną pewną ciekawą rzecz, że na samym dole takiego obiektu, czyli Array.prototype mamy coś takiego
jak _ _proto_ _
_ _proto_ _.
To z języka angielskiego jest double under nazywane również w skrócie dunder czyli dunder proto.
Mamy tutaj coś takiego i możemy tutaj przejść. I mamy znowu jakiś obiekt, bo widzimy to po tym. Co się
okazuje,
to, co widzimy tutaj, zawiera valueOf w tym miejscu. i to, co widzimy tutaj, jest to Object.prototype.
Gdybym zrobił dir(Object.prototype), to okaże się, że wewnątrz właśnie jest dokładnie to samo.
Mamy tam metodę valueOf. Czyli jeżeli tutaj chcieliśmy odwołać się do valueOf, nie ma jej tutaj, nie ma jej w Array.prototype,
to przechodzimy do Object.prototype.
No i znowu mógłbyś zapytać - dlaczego tak jest, że jeżeli nie ma tego w Array.prototype, to akurat przechodzimy do
Object.prototype, a nie gdzieś indziej.
To również można łatwo sprawdzić. Object.prototype, a dokładnie Array.prototype to jest obiekt.
Czyli gdybyśmy zapomnieli, gdzie weszliśmy zupełnie, że Object.prototype to jest jakiś dziwny zapis, to
nie jest w ogóle dziwny zapis.
To jest taki zwykły obiekt. I teraz gdybyśmy chcieli sprawdzić na takim obiekcie, jaki jest jego konstruktor.
W ten sposób. To się okaże, że Object.
Czyli kiedy przechodzimy do Array.prototype. W ten sposób. Dla nas zostaje tak naprawdę zwrócony
obiekt. I zapomnijmy, że jesteśmy w Array.prototype - to tutaj nie ma żadnego znaczenia.
Przeszliśmy do obiektu. I taki obiekt jako swój konstruktor ma Object.
Czyli jeżeli nie znaleźliśmy czegoś w Array.prototype, to szukamy dalej. Array.prototype jest obiektem, więc sprawimy
sobie, jaki konstruktor ma obiekt. Pokazałem ci tutaj, że jest to Object.
Z tego właśnie powodu przejdziemy sobie do Object.prototype i zobaczymy, czy taka właściwość tam się znajduje.
Taka metoda valueOf tam jest, więc ją zwracamy.
Mam nadzieję, że rozumiesz teraz, jak ten łańcuch działa.
Przy okazji chciałbym się jeszcze zatrzymać przy tym, czym jest właśnie proto,
czyli jeśli tu wpiszę np. Array.prototype,
dlaczego mamy tutaj dostęp do proto?
Takie coś czyli dunder proto, jest to nieoficjalna właściwość, czyli w specyfikacji ECMAScript
jej nie ma, ale w każdej przeglądarce internetowej to działa. W naszych aplikacjach nie powinniśmy z tego
korzystać.
To jest bardziej do takich celów testowo-naukowych możemy powiedzieć. Natomiast tak jak ci pokazałem,
jeżeli mamy konstruktor czyli funkcję np. Array, to możemy się odwołać do prototype.
Natomiast jeżeli już utworzymy jakąś tablicę np. w ten sposób - pustą tablicę, to nie możemy się odwołać
do prototype, ale do proto już możemy - w ten sposób. I będzie to kierować
dokładnie na Array.prototype,
gdybyśmy tutaj zrobili oczywiście dir. Czyli zapamiętaj, że .prototype jest dostępny na funkcji, czyli
na konstruktorze. Kiedy z tego konstruktora utworzymy sobie już jakiś obiekt, który jest nazywany instancją, to na
nim aby się odwołać do prototypu,
możemy to zrobić właśnie w taki sposób. I gdybyśmy teraz chcieli porównać, czy nasza tablica - czy jej proto to
jest dokładnie to samo co Array.prototype, to okaże się, że tak
w rzeczy samej jest. Teraz jeszcze jest kilka innych sposobów, gdybyśmy koniecznie chcieli z takiej instancji
tablicy odwołać się do prototypu. Nie możemy napisać .prototype, ale stwórzmy sobie taką tablicę raz jeszcze.
Możemy napisać constructor, który jak już wiesz zostanie na prototypie znaleziony.
Tak możemy napisać. Proszę bardzo.
Czyli w tym miejscu jesteśmy już jakby w Array, czyli tutaj. I teraz możemy dalej napisać prototype. I to znowu
będzie to samo, gdybyśmy tutaj chcieli sprawdzić, utworzymy nową tablicę.
Gdybyśmy chcieli sprawdzić, czy jej proto kieruje dokładnie w to samo miejsce, co tablica - constructor.prototype - to okaże
się, że w rzeczy samej
tak jest. I jest jeszcze jedna opcja, gdyby to nam było potrzebne - może być w pewnych sytuacjach programowania
obiektowego. Gdybyśmy mieli jakąś gotową już instancję, czyli taką tablicę, jak mamy tutaj, my również
ją mamy pod zmienną
arr - pamiętaj.
Gdybyśmy chcieli pobrać sobie jej prototyp, to mamy taką funkcję dostępną na Object.
I tutaj mamy getPrototypeOf - w ten sposób.
I tutaj musimy podać już konkretną instancję, czyli np. naszą tablicę. W ten sposób również pobierzemy sobie prototyp
tego obiektu, czyli tej tablicy. I gdybyśmy tutaj z kolei napisali, czy to jest dokładnie to samo, co Array.prototype,
to znowu okaże się, że to jest dokładnie to samo. Czyli mamy trzy sposoby -
Object.getPrototypeOf. Podajemy tutaj jakiś obiekt i jego prototyp zostanie zwrócony. Albo poprzez Array.prototype,
jeżeli wiemy, jakiego typu jest już ten obiekt.
Jeżeli nie wiemy, to możemy zawsze do tego obiektu się odwołać:
.constructor.prototype. Czyli ten zapis w tym przypadku jest taki sam jak ten zapis, i taki sam jak
ten zapis. Tutaj wszędzie zostanie zwrócone true.
Co jest ciekawe, jeżeli chodzi o to proto. Pokazywałem ci w prezentacji, że Object.prototype nie ma już swojego
prototypu. Czyli jeżeli czegoś szukamy - valueOf, sprawdzamy, czy jest tutaj. Nie ma.
Sprawdźmy czy jest w Array.prototype.
Nie ma.
Sprawdźmy w Object.prototype.
Jeżeli tam go nie ma, nie szukamy dalej.
Jeżeli tu znowu zrobimy dir(Object.prototype), w ten sposób. I zajrzymy, to okaże się, że obiekt, który został
zwrócony, ma różne metody, tak jak byśmy sobie je po prostu w tym obiekcie wpisali.
Ale nie ma już tutaj proto.
Czyli jeżeli chciałbym sobie napisać Object.prototype, tutaj zostanie zwrócony obiekt oczywiście.
No i taki obiekt powinien mieć proto,
prawda? Okazuje się, że jest to null.
Dzięki temu nie mamy nieskończonej pętli. Bo teraz wyobraź sobie, jak byśmy wyszukiwali jakieś właściwości.
Gdybyśmy sprawdzili valueOf -
nie ma jej na tablicy, nie ma w Array.prototype. Przejdź do Object.prototype - nie ma.
No i gdyby tutaj był kolejny prototyp, to byśmy przeszli do tego prototypu i tak robilibyśmy w nieskończoność.
A można by było powiedzieć, ok, Object.prototype, to jest obiekt - to co już ci mówiłem - to jest taki
zwykły obiekt. Czyli on musi mieć swój prototyp w obiekcie. Zostałby nam zwrócony znowu obiekt i tam nie
byłoby tej właściwości czy metody i znowu byśmy stwierdzili: ok, ale to, co nam zwróciłeś, ten prototyp, to
jest obiekt, czyli jego prototyp, to jest Object.prototype. I tak byśmy szukali w nieskończoność.
Natomiast tutaj zostało zwrócone null.
Czyli jeżeli w Objekt.prototype nie zostanie znaleziona jakaś właściwość czy metoda, to już nigdzie nie
zostanie znaleziona i zostanie nam zwrócone
undefined. Teraz chcę ci jeszcze pokazać, że prototypy są żywe.
Co to oznacza? Mniej więcej tyle, że jeżeli tutaj skorzystałem z arr2.valueOf i teraz napiszę sobie
Array.prototype.valueOf i przypiszę tutaj coś innego, jakąś funkcję, która zwraca powiedzmy false, to jeżeli raz
jeszcze będę chciał wykonać to, co miałem tutaj, zobaczmy. To mamy tutaj 3 i 5 - to jest zwrócone stąd.
Następnie odwołałem się do arr.prototype.valueOf i nadpisałem tę metodę. Czyli teraz, kiedy się odwołaliśmy
do arr.valueOf, zauważ - zostało zwrócone false. I tutaj znowu działa to dokładnie w ten sam sposób. Sprawdzamy,
czy valueOf jest bezpośrednio na tej tablicy.
Gdyby było, to to w ogóle nie zostałoby wzięte pod uwagę. Owszem, zmienilibyśmy to prototypie, ale tutaj zostałaby
z najkrócej drogi zwrócona metoda, która byłaby tutaj.
Nie ma jej, więc przeszukujemy prototyp Array. Wchodzimy do Array.prototype i szukamy, czy tam jest valueOf.
Okazuje się, że jest - taka, którą ja nadpisałem.
Czyli tutaj skorzystaliśmy z tej, która jest domyślnie valueOf w prototypie.
A tutaj już z tej, którą ja napisałem. Wynika z tego tylko tyle, że w trakcie wykonywania naszego skryptu
możemy dowolnie zmieniać sobie prototyp i tablica, która już została utworzona, bo nie mówimy tu o nowej
tablicy, którą teraz tworzę, ale ona już dużo wcześniej została utworzona,
ma dostęp do tego żywego prototypu i nadpisaliśmy taką metodę i teraz ona zwraca nam false. Już całkiem
na sam koniec tej lekcji - mam nadzieję, że wszystko dobrze zrozumiałeś - pokażę ci jeszcze jedną metodę
isPrototypeOf.
Jeżeli mamy jakiś obiekt i chcielibyśmy sprawdzić, czy jest on prototypem innego obiektu, to możemy to
zrobić. isPprototypeOf jest dostępne na każdym obiekcie
z prostego powodu. Otóż możemy się odwołać do Object. Może zróbmy to znowu w dir.
Jeżeli wejdziemy do Object.prototype, czyli tego już na samej górze nadrzędnego obiektu. On ma różne metody
i ma tutaj taką metodę jak isPrototypeOf.
Mam nadzieję, że rozumiesz, że skoro w Object.prototype jest taka metoda, to każdy obiekt w języku JavaScript -
czy to będzie string, czy to będzie tablica, czy to będzie data, ma dostęp do czegoś takiego. Bo jeżeli
na obiekcie daty nie zostanie to znalezione, to idziemy w górę aż do Object.prototype.
Skoro tu jest taka metoda, to każdy obiekt ma taką metodę. Czyli isPrototypeOf jest dostępna na każdym obiekcie.
To oznacza, że jest dostępna też na takim zwykłym obiekcie, który utworzymy.
Natomiast teraz, jeżeli przejdziemy sobie np. do Array.prototype.isPrototypeOf. Tak jak wspomniałem, na każdym obiekcie
jest to dostępne. Czyli zapomnijmy, że jesteśmy tutaj.
Ale zastanówmy się, co zostało zwrócone. Array.prototype zwraca obiekt, a każdy obiekt zawiera metodę isPrototypeOf
właśnie z prototypu, więc możemy z niej skorzystać. I teraz możemy napisać
arr. Zostało zwrócone true.
Czyli tutaj zapytaliśmy, czy ten obiekt, który tu jest zwrócony, jest prototypem naszej tablicy, która jest
utworzona.
Okazuje się, że tak.
Natomiast jeżeli zrobimy to samo z Object.prototype, to również będzie true,
czyli Object.prototype - ten obiekt, który tam się znajduje, też jest prototypem naszej tablicy.
Oznacza to tylko tyle, że jeżeli szukamy jakiejś metody czy właściwości na tablicy, to ona zarówno tutaj w
Arrau.prototype będzie wyszukiwarka, jak i w Object.prototype, bo obydwa te obiekty są prototypem tablicy.