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 lekcji poprzedniej umówiłem ogólne koncepcje programowania zorientowanego obiektowo, które znajdziesz
w językach takich prawdziwie obiektowych, jak PHP, C++ czy np. Java.
Natomiast język JavaScript w tym klasycznym rozumieniu, nie jest takim czysto obiektowym językiem programowania.
Ma za to mechanizm, który nazywa się dziedziczeniem prototypowym i chciałbym go teraz omówić w tej lekcji.
W języku angielskim
dziedziczenie prototypowe znajdziesz pod nazwą prototypal inheritance.
I teraz wyobraź sobie, że utworzymy sobie nową tablicę w taki znany nam już, prosty sposób. Wiesz, że możemy robić
to na dwa sposoby - albo takie nawiasy kwadratowe, albo możemy napisać new array.
I teraz możemy odwołać się na takiej tablicy do metody indexOf, podaję tutaj jedynkę i oznacza to, że
chcę sprawdzić, czy w takiej tablicy znajduje się jedynka. Zostało tutaj zwrócone zero, czyli pod indeksem zerowym
jedynka się znalazła.
Nic skomplikowanego. Natomiast zastanówmy się, skąd w ogóle w takiej tablicy znalazła się metoda
indexOf.
Wiemy, że jest ona natywnie przez JavaScript wspierana. Jest dla nas dostępna i w zasadzie można by
było stwierdzić - a co nas to obchodzi, skąd ona tam się wzięła.
Natomiast kluczem do zrozumienia języka JavaScript jest zrozumienie dziedziczenia prototypowego.
I to ma z tym bardzo wiele wspólnego. Metoda indexOf nie znajduje się bezpośrednio na naszej tablicy,
co za moment ci udowodnię.
Na początek spróbujmy sobie utworzyć znowu taką samą tablicę, ale tym razem korzystamy z konstruktora new Array.
Gdybyśmy sprawdzili type of Array, to będzie to function.
Nie mówię tutaj o naszej zmiennej, ale o samym zapisie Array z dużej litery. Czyli na funkcjach możemy wykonywać
new, czyli używać takiego operatora.
Tutaj zostanie zwrócona dokładnie taka sama tablica i znowu na niej możemy skorzystać z arr.indexOf.
Natomiast w poszukiwaniu naszej metody indexOf -
gdzie ona jest?
Moglibyśmy zajrzeć do arr.indexOf bez tworzenia nowej tablicy, czyli po prostu do tej funkcji.
Okaże się, że zostanie zwrócony
undefined. Czyli skąd nasza tablica wzięła sobie taką metodę, skoro nawet na konstruktorze ona nie jest
dostępna.
Otóż metoda ta znalazła się w tak zwanym obiekcie prototypu.
Jeżeli wpiszemy Array.prototype, to zostanie nam zwrócony obiekt, jak tutaj zaznaczyłem w komentarzu.
Taki zwykły obiekt jak my sobie możemy utworzyć poprzez parę takich nawiasów klamrowych. Czyli każda funkcja,
bo to jest bardzo ważne, że Array to jest funkcja.
Każda funkcja zawiera taką właściwość jak prototype i tam jest zawsze obiekt.
Jeżeli do tego obiektu sobie zajrzymy, to okaże się, że tam znajduje się metoda indexOf.
Jest to funkcja, która przyjmuje parametr i zwraca nam index w tablicy,
czy taki parametr tam został znaleziony.
Nie możemy zajrzeć już do tej funkcji, jak ona natywnie została zaimplementowana, ale tutaj jest bardzo
ważne, że znajduje się ona właśnie w Array.prototype.
Kolejna bardzo ciekawa sprawa jest taka, że na tablicach możemy skorzystać z takiej metody jak valueOf.
W zasadzie wszystko, co w JavaScript jest obiektem, ma dostęp do takiej metody.
I znowu mógłbyś sobie pomyśleć, że znajdziemy taką metodę w Array.prototype.
Jeszcze zanim przejdę dalej, powiem tylko tylko, że metoda valueOf zwróci nam po prostu naszą tablicę, czyli
w przypadku tablic ona nie jest zbyt przydatna. Ale tak jak wspomniałem, pomyślałeś sobie teraz:
no ok, skoro indexOf jest w Array.prototype,
valueOf zapewne też.
Otóż nie.
Metoda valueOf znajduje się w Object.prototype, właśnie w tym obiekcie.
Tam oczywiście będzie jeszcze więcej metod, tak jak w przypadku Array.prototype - nie tylko indexOf, ale
wszystkie metody dostępne na tablicach i w Object.prototype również będzie wiele innych metod.
Natomiast samo Object pisane z dużej litery musisz zapamiętać.
To również jest funkcja, bo możemy napisać new Object i zostanie dla nas utworzony nowy obiekt w taki
sam sposób, jak byśmy sobie napisali ten zapis -
dwa nawiasy
klamrowe, czyli tablicę też możemy tworzyć na dwa sposoby,
podobnie jest z obiektami. Czyli w Object.prototype zostało to znalezione.
Dlaczego tak się dzieje -
ja ci jeszcze wytłumaczę, ale przejdźmy teraz do kolejnego przykładu. Znowu będziemy mieli naszą tablicę
i tym razem do tej konkretnej tablicy dopiszę sobie metodę indexOf.
W pierwszej części tego kursu mówiłem, że w JavaScript prawie wszystko jest obiektem, więc do prawie wszystkiego
możemy napisać kropka i odwołać się do jakiejś metody czy cokolwiek sobie przypisać w dowolnym momencie
wykonywania naszego skryptu.
Więc jeżeli utworzyłem sobie tutaj nową tablicę, która jest obiektem, to mogę napisać arr.indexOf i zapisać
tutaj funkcję, która zwraca 0.
I teraz za każdym razem, jak będę chciał odwołać się do arr.indexOf i nawet podam jakiś parametr, to
już zostanie wykonana nasza funkcja, bo dokładnie na to kieruje
nam indexOf. Teraz jeżeli sobie utworzę nową tablicę po wykonaniu już tego kodu, będzie to arr2 i na
niej skorzystam z indexOf,
to nie zostanie użyta metoda, którą mamy u góry, ale odwołamy się do Array.prototype i tam znajdziemy
taką metodę. Mógłbyś się zastanawiać dlaczego.
Otóż tak działa przeszukiwanie prototypów.
Czyli jeżeli przypiszę sobie jakąś metodę bezpośrednio do obiektu, to jest szukana najkrótsza możliwa droga,
tak jak tutaj wyżej napisałem arr.indexOf i sprawdziliśmy, czy nasz obiekt Array, czyli ta tablica, zawiera
taką metodę indexOf. Sprawdziliśmy,
jest taka metoda, więc ją wykonaj i ona zawsze będzie zwracać zero, czyli jest bezużyteczna w tym przypadku.
Natomiast poniżej utworzyłem drugą tablicę, odwołałem się do arr2.indexOf i podałem piątkę. I znowu zostało
sprawdzone, czy na tym obiekcie jest taka metoda. Nie ma jej, bo ja tylko u góry do jednego obiektu ją przypisałem. Więc przejdźmy
dalej, do Array.prototype. Wiesz już, że jest to obiekt i w nim taka metoda się znajduje.
Natomiast co dzieje się, jeżeli odwołamy się np. do arr2.valueOf, czyli do tej metody, którą pokazałem
ci wcześniej?
Znowu będziemy chcieli sprawdzić, czy na tablicy arr2 jest taka metoda.
Gdybym ją przypisał tak, jak tutaj u góry - nie ma.
Więc przejdźmy do Array.prototype i sprawdźmy, czy tam się taka znajduje.
Okazuje się, że w Array.prototype nie ma metody valueOf, ale Array.prototype jest obiektem. To jest taki zwykły obiekt.
Więc taki obiekt też ma jakiś swój prototyp i przejdziemy dalej.
Okazuje się, że prototypem takiego obiektu jest object.prototype i ta metoda valueOf już zostanie
znaleziona i zostanie zwrócona. Na koniec tego przykładu pokażę ci jeszcze, że jeżeli odwołamy się do
array2.mySuperMethod, czyli będę chciał wywołać jakąś metodę, która nie istnieje, to będziemy mieli zwrócone
undefined. Konkretnie, gdybym użył na końcu tej pary nawiasów okrągłych, to mielibyśmy błąd, bo chcę wywołać
metodę, która nie istnieje.
Gdybym pominął te nawiasy, to mielibyśmy po prostu undefined, bo taka właściwość nie istnieje. Natomiast
droga sprawdzania jest tutaj dokładnie taka sama.
Czyli odwołując się do mySuperMethod sprawdzamy, czy arr2 ma bezpośrednio do tego obiektu przypisaną
taką metodę.
Nie ma, więc sprawdzimy Array.prototype. Tam również tego nie ma.
Sprawdźmy dalej, Object.prototype - tam również tego nie ma i dlatego zostanie zwrócony
undefined.
Idąc tym tokiem rozumowania, można się domyślić, że Object.prototype jest ostatnim przystankiem w tym łańcuchu
prototypowym.
Czyli gdybyśmy chcieli sprawdzić dalej po Object.prototype jakiś inny prototyp,
tam już nic nie ma i zostaje nam zwrócone null.
Z tego wynika tylko jedno, że łańcuch prototypów wygląda następująco. Na samej górze mamy Object.prototype
i samo prototype właśnie jest takim obiektem.
Natomiast pod spodem mamy bardzo wiele różnych konstruktorów, które w języku JavaScript są dostępne. To
tylko niektóre z nich, które jeżeli przerabiałeś pierwszą część tego kursu, już znasz, takie jak Array,
Function czy Number. I wiesz, że wszystkie te obiekty mają różne swoje metody. Ja tutaj po dwie z nich przytoczyłem.
Czyli jeżeli utworzymy tablicę za pomocą tego zapisu z nawiasami kwadratowymi albo new Array, to ma ona
dostęp do forEach czy indexOf.
Z tego powodu, że każdy z wymienionych tutaj na zielono konstruktorów ma po kropce prototype, czyli RegExp
czy Function czy nawet Number, jeżeli odwołamy się do Number.prototype,
to tam będą się znajdować różne metody, w tym te dwie, które tutaj pod Number wypisałem.
I zawsze jeżeli na jakiejś liczbie będziemy chcieli się odwołać do .toFixed, to zostanie sprawdzony
właśnie ten prototyp, czyli Number.prototype.
Jeżeli tam nie zostanie taka metoda znaleziona, to przejdziemy do Object.prototype, który jest na samej
górze.
Więc w przypadku JavaScript ten łańcuch prototypowy jest zazwyczaj dość krótki
w przypadku tych wbudowanych obiektów. Podobnie będzie z Date.
Jak widzisz, jeżeli utworzymy nową datę, odwołamy się do getDate, zostanie nam to bezpośrednio zwrócone.
Jeżeli odwołamy się do innej metody np. valueOf, która tam nie jest dostępna, to przejdziemy jeden krok
wyżej, do Object.prototype i to zostanie zwrócone.
Ten łańcuch jest dosyć krótki, ale my będziemy mogli go bez problemu wydłużyć, co ci jeszcze w tym kursie
pokażę. Natomiast co jest niezwykle ważne?
Wszystkie zaznaczone tutaj na zielono konstruktory, łącznie z Object,
są to funkcje.
Czyli gdybyśmy sprawdzili typeOf Array, to będzie funkcja. Jeżeli utworzymy z tej funkcji nową tablicę,
to jest już to obiekt.
Natomiast sam konstruktor jest to funkcja. Podobnie ze String, Boolean czy Date.
Z tego powodu musisz zapamiętać jedną rzecz, że tylko funkcja może mieć prototyp i jeżeli utworzylibyśmy
sobie nową funkcję, którą nazwę Person - jak widzisz pusta funkcja, nic nie robi - to już w tym momencie
po jej utworzeniu mogę się odwołać do Person.prototype i to też będzie pewien obiekt.
Domyślnie ten obiekt po utworzeniu jakiejkolwiek funkcji będzie wyglądał następująco, czyli będzie zawierał
jedno pole, jedną właściwość konstruktor, która kieruje dokładnie na taką funkcję.
Czyli gdybyśmy się odwołali do Date.prototype, to tam również będziemy mieli konstruktor, który kieruje
na date.
Jeżeli napiszemy Date.prototype.constructor, to jest to dokładnie to samo co date.
Z tego co tutaj widzisz, wynika jedna bardzo ważna sprawa.
Otóż jeżeli utworzyliśmy nową funkcję Person i Person.prototype to jest obiekt, w którym mamy tylko konstruktor.
To skoro jest to obiekt,
to oznacza, że do tego obiektu możemy dodawać dowolne rzeczy, jakie tylko chcemy
i one będą później dziedziczone przez wszystkie instancje, jakie tworzymy z tego obiektu.
Na razie jeszcze nie rozumiesz, co to oznacza, ale dokładnie ci to to pokażę.