Nowy Standard JavaScript
10 godz. 43 min · JavaScript · Full-stack i Programowanie
Piotr PalarzWeb DeveloperNa początku dowiesz się jakie nowości pojawiły się w języku, jeśli chodzi o tworzenie zmiennych i stałych. Poznasz dwa nowe słowa let i const, a także dokładnie omówimy ich różnice, względem znanego nam do tej pory słowa var. Następnie poruszymy temat obiektów. Zobaczysz jak nowy zapis pozwoli znaczenie ułatwić nam pracę i sprawić, by kod był bardziej czytelny. W kolejnym rozdziale porozmawiamy o zmianach w funkcjach. Poznasz tzw. arrow functions wraz z poradami, kiedy z nich korzystać, a także z konsekwencjami ich użycia. Zobaczysz również, jak korzystać z domyślnych parametrów. Następną nowością będą operatory rest i spread, które znacząco uproszczą pisanie kodu dla funkcji i tablic. Chwilę później rzucimy okiem na bardzo ciekawy temat, mianowicie na tzw. destructuring. To nowa funkcja języka, która pozwala w łatwy sposób wyciągnąć odpowiednie dane z tablic i obiektów, nawet jeżeli te posiadają kilkupoziomową, zagnieżdżoną strukturę. Zmian doczekały się również ciągi znaków. Poznasz nowy zapis, tzw. template string, a także możliwość wykorzystania funkcji tagujących.
ES6 wprowadza do języka JavaScript zapis klas. I choć na początku wydaje się, że to duży krok w stronę klasycznych obiektowych języków programowania, to jak się przekonasz, jest to w dużej mierze tzw. syntactic sugar, a więc zupełnie nowy, prostszy zapis, a prawie identyczne jak wcześniej działanie, oparte na dziedziczeniu prototypowym. W kursie poruszymy również temat Symboli oraz tego jak z nimi pracować, a chwilę później poznasz iteratory. To konstrukcje ułatwiające iterację, które znacząco upraszczają pracę z kolekcjami danych. Wraz z iteratorami poznasz również generatory, które ułatwiają z kolei tworzenie tych iteratorów. Istotną nowością w ES6 są również natywne Promise. To sposób na rozwiązanie problemu asynchroniczności, który znamy od bardzo dawna w postaci różnych bibliotek. Od teraz jednak Promisy znajdują się bezpośrednio w języku. Nowymi konstrukcjami, które pozwalają pracować z kolekcjami danych, są Map i Set. Dowiesz się jak z nich korzystać, a także do czego są przydatne. W jednym z rozdziałów rzucimy też okiem na tzw. Proxy API. To mechanizm języka, który pozwala na dość niskopoziomowy dostęp do różnych operacji. I choć być może nie jest to coś, z czego będziesz korzystał nad wyraz często, to warto wiedzieć, jakie są nowe możliwości tego API.
Jedną z najlepszych nowości, która zawitała do języka JavaScript wraz z ES6 są moduły. To możliwość dzielenia kodu na wiele plików, z których można pewne wartości wyeksportować, a następnie zaimportować je w innych modułach. Tutaj dowiesz się wszystkiego, co z modułami związane. Zobaczysz wiele sposobów na eksport i import, a także jak pracować z modułami w przeglądarkach internetowych. Kurs skupia się na omówieniu wszystkich najważniejszych nowości w ES6, dzieląc je na rozdziały. Jeden z nich natomiast, poświęcony został omówieniu mniejszych zmian, które znalazły się w specyfikacji. Poznasz w nim np. nowe metody tablic czy ulepszoną współpracę z Unicode.
Specyfikacja EcmaScript 2015 wprowadziła naprawdę bardzo wiele zmian, ale jej twórcy założyli, że od tego czasu każda kolejna wersja specyfikacji będzie pojawiać się co roku. Oznacza to przede wszystkim, że nie będziemy na raz bombardowani aż tak wieloma zmianami, jak w 2015 roku. W tym kursie nie zabrakło zatem omówienia nowości ze specyfikacji ES2016 oraz 2017, gdzie poznasz np. nowe metody dla stringów czy funkcje asynchroniczne.
Kiedy będziesz już uzbrojony w solidną wiedzę z zakresu nowości w ES6, dowiesz się jak w praktyce pracuje się dzisiaj z tym standardem. W jednym z rozdziałów omówimy nowoczesny workflow z użyciem npm i wykorzystamy popularne narzędzie webpack, do pracy z modułami, a także do transpilowania kodu ES6 do wersji ES5. Dzięki temu nowoczesny kod, który napiszemy, będzie mógł być uruchamiany również w starszych przeglądarkach, które nie wspierają najnowszego standardu. Dowiesz się na co zwrócić uwagę, by wszystko działało bez zarzutu.
Zwieńczeniem tego kursu będzie praktyczny projekt, w którym od podstaw wykonamy swoją własną bibliotekę do pracy z Obiektowym Modelem Dokumentu. Będzie ona w działaniu nieco podobna do znanej chyba wszystkim biblioteki jQuery. Na tym przykładzie postaramy się wykorzystać jak najwięcej wiedzy z zakresu ES6 zdobytej wcześniej. Stworzymy metody do pracy z drzewem dokumentu, a więc do wyszukiwania elementów, do tworzenia nowych, do przypisywania im klas, atrybutów czy styli CSS. Nie zabraknie również modułu odpowiedzialnego za współpracę z Ajax. Cały kod utworzymy oczywiście w sposób modułowy, aby bibliotekę można było łatwo rozwijać w przyszłości. Na koniec projekt ten przetranspilujemy do kodu ES5 z użyciem Babel i przetestujemy w starszej przeglądarce Internet Explorer 11, aby upewnić się, że nasza biblioteka działa poprawnie.
Kurs ten jest dla wszystkich osób, które znają już język JavaScript i czują się w pracy z nim swobodnie. Jeśli zatem chciałbyś pracować z nowoczesnym kodem JavaScript, to ten kurs jest zdecydowanie dla Ciebie. A może masz zamiar poznać framework Angular lub bibliotekę React? Tam również wykorzystywany jest najnowszy zapis ES6, więc wiedza ta będzie naprawdę pomocna. Z solidną wiedzą z zakresu EcmaScript 6, bez trudu pozostaniesz na bieżąco z każdą kolejną edycją tej specyfikacji.
W tej lekcji pokażę ci, na zakończenie nauki o promise, jak one świetnie
mogą współpracować z generatorami. Widziałeś już przykład gdzie wykorzystaliśmy
generatory i Ajax i dzięki temu mogliśmy sobie stworzyć jakby taką synchroniczną
funkcję, która można powiedzieć, że pauzowała się i czekała aż otrzymamy
odpowiedź, czyli pracowaliśmy z Ajax'em w taki sposób synchroniczny, a nieasynchroniczny. Było
tam jednak sporo kodu i troszeczkę może dziwnie to wyglądało, natomiast w przypadku promise,
będzie to można znacznie ułatwić i zrobić w sposób bardziej dynamiczny,
więc teraz będę chciał ci to pokazać. Przejdźmy sobie zatem do kodu i mamy
tutaj funkcję get json, którą mieliśmy wcześniej, ona operuje na promise, czyli
nie będziemy jej zupełnie zmieniać. W tej funkcji fajne jest to, że ona
po prostu jest uniwersalna i już w kilku lekcjach z niej korzystaliśmy i nawet w tej lekcji nie będziemy
musieli zupełnie nic tutaj modyfikować. Natomiast po kliknięciu na przycisk chciałbym
móc wywołać pewną funkcji generatora, którą za moment stworzymy. Powiem
ci od razu jak chciałbym żeby ta funkcja wyglądała. Wpiszę ją tutaj na szybko w sposób anonimowy. Zaraz
ją w pewne miejsce przekażemy. Pamiętasz, że z gwiazdką to jest generator i chciałbym
móc mu przekazać jakiś adres URL i wewnątrz chciałbym móc pracować synchronicznie,
to znaczy chciałbym napisać let json równa się get json
i następnie URL. Natomiast przed tym zrobimy sobie yield, które
już znasz i to oznacza, że z tej
funkcji generatora zwrócimy to, co zwróci get json. Wiemy, że get json zwraca promise,
więc zwrócimy to kiedy skorzystamy z iteratora i
metody next. W razie czego sobie wróć do tej lekcji gdzie o tym mówiłem i
tam dopiero wtedy, kiedy zrobimy sobie promise resolve, to wartość
zostanie wstawiona w to miejsce. Zaraz to będzie dokładnie widać, ale piszę to
w sposób synchroniczny, czyli mówię. Tutaj pobierz mi json, a w kolejnej linijce
chciałbym sobie móc to wstawić na stronę do elementu pre-43
textContent równa się json,
czyli ja chcę wpisać kod w sposób synchroniczny i chciałbym, żeby to było na tyle
uniwersalne, żebym mógł później napisać json 2 i tak dalej, ale do
tego wrócimy, czyli musimy sobie tę funkcję gdzieś przekazać, dlatego
wytnę ją i stworzę funkcję run. To będzie funkcja uniwersalna
jak się przekonasz i jako pierwszy parametr będzie przyjmować generator. Przekażmy
mu go tutaj, a jako drugi i każdy kolejny dowolne inne parametry,
więc tutaj powiedzmy, że będziemy chcieli sobie przekazać parametr
z adresem, pod który wysyłaliśmy wcześniej te zapytania. Okej,
czyli naszym celem będzie stworzenie funkcji run, która przyjmie jako pierwszy parametr
funkcje generatora, wywoła ją przekazując jej wszystkie parametry, które
podamy tutaj. Czyli drugi, trzeci i tak dalej. Stwórzmy sobie ją zatem. Napiszmy run i
pierwszy to będzie get, jak generator, czyli cała ta funkcja wpadnie mi pod
gen, a drugi, trzeci i kolejny chcę sobie operatorem rest zebrać jako
args i w środku będę chciał utworzyć iterator pisząc let it i pamiętasz,
że tworzymy iterator wywołując generator, czyli skoro pod gen mam generator,
to chcę go wywołać, ale że ten generator oczekuje, że będzie miał tutaj URL,
to skąd go wezmę. Mam go w args i URL i każdy inny argument,
który bym tutaj przekazał. Więc operatorem spread będziemy chcieli je
tutaj wstawić. Mamy zatem pod it jakiś iterator. No i teraz jeżeli
bym wywołał it next, to co się stanie w tym konkretnym przypadku. Otóż yield zrobi
kiedy json i zwróci mi promise, więc teoretycznie gdybym napisał
let p it next, to to zwróci nam obiekt, w którym mamy
value i done, jak pamiętasz, więc po kropce jak się odwołam do value, to pod p powinienem
mieć promise i zróbmy sobie console.log p. Ok,
klikam przycisk i pod p mam promise, czyli na razie jest wszystko w porządku.
Tylko, że teraz ja bym musiał wiedzieć, że mając już ten promise, żeby
przejść tutaj do kolejnej linijki a pod to coś podstawić to muszę do tej promise
przypisać sobie then, pamiętasz
i na then wywołałbym sobie funkcję, która by
mi zrobiła iterator next i to co byłoby tutaj, czyli
jakiś json pobrany przekazała dalej. Jeżeli to za szybko
teraz mówię, to też na spokojnie sobie to prześledź. To jest dokładnie to co do tej pory
w poprzednich lekcjach pokazywałem, czyli jeżeli do promiski przypisze funkcję then,
to mówię kiedy ona będzie mieć resolve, czyli w tym miejscu i przekażę
jakieś dane, to ja chcę mieć te dane pod json i wywołać wtedy tę funkcję,
a w tej funkcji wywołam iterator next przekazując mu json. Pokazywałem
ci właśnie w lekcji, w której można było przekazywać do iteratora
jakieś dane, do tej funkcji generującej tak naprawdę do środka, że tak to wygląda, więc
jeżeli zrobimy yield get json, to zwróci nam to tutaj promise,
ale kiedy później zrobimy iterator next i coś przekażemy, to wrócimy w
to miejsce i w tym miejscu to postawimy, czyli już gotowe dane. No i gdybyśmy
tylko tyle zrobili, to powinno nam to zadziałać. Zobaczmy, dane się wyświetlają,
więc tak można stworzyć funkcję działającą synchronicznie. Z tym, że ja tutaj musiałem
dokładnie wiedzieć, co jest w tej funkcji, Ile jest tutaj yield'ów. Natomiast teraz
chciałbym ją zrobić w sposób dynamiczny, aby tutaj można było dodawać ile
tylko chcę takich yield'ów i aby to wszystko działało już automatycznie, a więc
zrobię funkcję run. Mam funkcję get json i nie chcę ich potem zupełnie zmieniać, a
jedyne co chce zmieniać, to funkcje generatora. Żeby
to zrobić usuniemy to co mieliśmy w tym miejscu i na górze gdzie utworzyłem zmienną it utworze
jeszcze drugą, która będzie się nazywać result, ona nam zaraz będzie potrzebna
i teraz utworzę sobie funkcję, którą nazwę next. Nie ma to nic wspólnego z
next na iteratorze, możesz ją nazwać jak chcesz i będę chciał ją wywołać.
I co ona zrobi? Wywoła nam iterator next i to
co zwróci zapiszę nam do result, ale pamiętasz, że next
może tutaj przyjmować jakieś wartości, które przekażemy do generatora, dlatego
wpiszę sobie tutaj value i chciałbym żeby ona też przyjmowała value, czyli
na razie prześledźmy raz jeszcze zdefiniowałem funkcje i ją wywołałem. Wykonamy
zatem it next nie przekazałem parametru, więc ma tutaj on wartość undefined. Nic
się nie wydarzy, po prostu wykona się ta linijka gdzie yield wyrzuci
nam promise zwróconą w get json, czyli pod result będę miał
obiekt, w którym jest done false, a value jest to ta promise. Raz
tylko to wykonałem i teraz będę chciał sprawdzić czy result
done nie ma jeszcze
wartości true, czyli jeżeli jeszcze nie zakończyliśmy, bo tutaj na przykład jest jeszcze kolejny yield
to znaczy, że chcemy wykonać jakiś kod. W tym
miejscu będę chciał sprawdzić czy to co zostało zwrócone jest promise'm, czyli
skąd będę to wiedział. Pod result jest cały obiekt, gdybyś zrobił console.log, więc
result value to jest ta promiska. Jak sprawdzić czy to co zostało zwrócone
z tego miejsca, czy to rzeczywiście jest promise, a nie jakaś inna wartość. Otóż
zrobimy sobie tutaj coś takiego, if i wkleję
to w tym miejscu, result value then i zrobimy
na tym operator typeof, czyli jeżeli pod result value
mam promise, no to then, powinno to być function, więc
sprawdzam czy po prostu to co jest pod result value jest funkcją, czy
zawiera then i czy jest to funkcja. Jeżeli tak, to wiem, że pracuje na promise. Przypiszmy
sobie zatem do tej promise obsługę tego jakby handler'a then.
Więc result value jest to promiska. Na then chcemy
wywołać jakąś funkcję i teraz pytanie jaką. Tutaj się robi taka rekurencja,
że można się w tym zagmatwać dlatego na spokojnie sobie na to wszystko patrz. W razie czego
pauzuj, ale jeszcze raz gdybyśmy sobie to prześledzili. Przekazujemy
tutaj generator. Wykonuje go w tym miejscu i mam iterator, definiuje
funkcję next, nic się nie wydarzyło, ale teraz ją wywołałem, więc wywołaliśmy
it next. To sprawiło, że ten yield się wykonał. Uruchomił
tak naprawdę get json, który zwrócił promise i ten yield nam zwraca to promise do
result. Ja sobie sprawdzam czy już zakończyliśmy, czy mamy done na true, nie. Więc
teraz sprawdzam czy to co mi ten yield zwrócił, zawieramy metodę then. Okazuje się,
że tak, więc wiem, że jest to promise. Dlatego wywołuje tę metodę then,
tak jak to robiliśmy na promise'ach i mówię jeżeli ta promise będzie miała kiedyś w
przyszłości status resolve i przekażę tam jakieś
dane, to wtedy wywołaj moją funkcję przekazując jej te dane. A jaką
funkcję tutaj podałem. Otóż tę samą funkcję next, a dane chcę mieć pod
value, czyli jeżeli tutaj będzie resolve, to wykona się od
nowa jakby ta funkcja next gdzie pod value będzie miała to, co nam już
zwrócił Ajax i ona znowu wykona iterator next, przekazując
mu te dane, a więc one wpadną w to miejsce, bo ten generator nam wyrzucił
coś i czeka na kolejne użycie next żeby przyjąć jakąś wartość. Przyjmie
wartość tego jsona tutaj i będzie sobie dalej wykonywał swój kod wstawiając
json na stronę, więc tak to będzie wyglądać, ale on przy okazji zwróci
nam kolejny obiekt, w którym będziemy mieli value. W
tym przypadku już undefined i done to będzie
nadal false, czyli nasz warunek jeszcze raz byś ją wykonał, ale że to co zostanie
zwrócone nie zawiera już metody then, ta linijka nigdy się nie wykona i tak
naprawdę nic więcej się tutaj nie wydarzy. Nie ustawimy kolejnego wykonania
funkcji next. No więc sprawdźmy czy to zadziała, okazuje
się, że tak, więc prześledź sobie to jeszcze raz w razie czego. Natomiast fajne jest to, że tutaj korzystamy
tak naprawdę z takiej można powiedzieć o rekurencji i to o tyle jest
przydatne, że jeżeli teraz chciałbym stworzyć zmienną json 2 i do
tego URL'a na przykład dopisać sobie shuffle 1, wysłać
kolejne zupełnie zapytanie, to tutaj cały czas pracuje synchronicznie. Tę funkcję
mogę ukryć, powiedzmy, że w ogóle jej nigdy nie widziałem. Wiem tylko jak działa, tak
samo tę, nie ma ich. Wpiszę sobie tylko taki kod mówiąc uruchom
mój generator przekazując mu taki argument, pod URL, a w nim sobie
piszę, że chce Ajax'em pobrać takie dane, takie dane i kiedy je mam,
dopiero przechodzę do tej linijki, a w tej linijce mógłbym tak jak w poprzednich
lekcji, wyświetlić dane i jedno i drugie, dlatego skopiuję teraz taki fragment,
czyli w template stringu moglibyśmy wyświetlić
to co jest w json, następnie dwa nowe wiersze, znak
równa się 70 razy powtórzony, dwa nowe wiersze i json 2. Tylko w tej funkcji coś
zmieniłem i udało mi się pobrać dwa niezależne, jak
widzisz, zapytania wysłać do serwera, tutaj są rozdzielone i
wszystko działa poprawnie, więc w ten sposób można również korzystać z generatorów. No
i tą lekcją kończymy sobie poznawanie generatorów. Zobaczyłeś, że one są niezwykle przydatne,
bo zwracają iterator i na przykład mogliśmy je dodać do naszej klasy po to,
aby jeżeli ktoś wykorzysta na naszym obiekcie operator spread czy
na przykład pętle for off. Żeby poszczególne elementy z tej naszej klasy zwracać. Natomiast
okazuje się, że generatory są przydatne również w przypadku pracy w takim
asynchronicznym kodem dzięki właśnie temu yield, które pauzuje wykonywanie
dalszego kodu i jeśli odpowiednio do tego dopiszemy sobie taką funkcję, jak na przykład
tutaj, to możemy pisać kod, który wygląda jak kod synchroniczny, a więc
czeka aż otrzyma jakieś dane i dopiero wtedy przechodzi do kolejnej linijki.
Wiem, że jest to temat dosyć skomplikowany, dlatego raz jeszcze na sam
koniec zachęcam, na spokojnie przerób to sobie jeżeli nie wszystko dobrze zrozumiałeś,
a tymczasem zapraszam cię już do kolejnych lekcji.