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, na koniec omawiania klas jeszcze garść ciekawostek odnośnie
tych klas. Ja ci wspominałem wcześniej, że w gruncie rzeczy nowy zapis klas,
to jest syntactic sugar, czyli to wygląda inaczej niż wcześniej, ale działa tak samo, aczkolwiek już
wspomniałem o kilku istotnych różnicach. Między innymi takich, że
jeżeli mieliśmy ten stary zapis w EcmaScript 5, no to na przykład funkcji są hoistowane
do góry czy jeżeli jesteśmy w globalnym zakresie, są dodawane jako właściwości globalnego
obiektu. Natomiast w gruncie rzeczy stwierdziłem, że taki zapis jak tutaj, to
jest praktycznie to samo, co mamy w tym miejscu, czyli funkcja sayHello zostanie
dodana tutaj na prototypie. Konstruktor zostanie wywołany, czyli to co jest
tutaj, dokładnie tak jak tutaj i tak dalej. Natomiast jest jedna bardzo
ważna różnica w tym starym zapisie, a w tym nowym, którą chciałbym ci teraz pokazać. Musimy
sobie to wykomentować i zauważ, że utworzyłem nowy obiekt z tego starego zapisu
i co by było gdybym teraz pętlą for, chciał wyświetlić wszystkie
jego klucze, czyli na przykład let in, jest to pętla for in, czyli operator in,
person1 i w konsoli chcę wyświetlić wszystkie klucze.
Zobaczmy. Okaże się, że mamy firstName, lastName i sayHello. No i sayHello jest to
metoda, która nie jest dodana bezpośrednio do tego obiektu, bo o ile firstName i lastName
poprzez this są przypisane bezpośrednio do tej instancji, o tyle sayHello jest
na prototypie, ale domyślnie metody dodawane na prototypie w ten sposób,
są enumerable. To oznacza, że one są wyliczane i na
przykład ten key, która korzysta z operatora in, będą widoczne. Natomiast,
gdybyśmy zrobili to samo z użyciem klas, zobacz. Też
dodamy sayHello na prototypie, ale okaże się, że tutaj nam się to już nie pojawia,
czyli domyślnie dodane do prototypu klas metody,
za pomocą takiego zapisu jak tutaj, są non enumerable, czyli niepoliczalne
i teraz pytanie czy moglibyśmy sprawić żeby w ES5 też były niepoliczalne. Okazuje
się, że tak i chciałbym ci teraz to szybko pokazać. Nie dodawalibyśmy funkcji do prototypu
w taki sposób jak to było zrobiłem, tylko musielibyśmy skorzystać z zapisu
dostępnego w ES5 poprzez Objects i następnie define
property, czyli można powiedzieć zdefiniuj właściwość.
Tutaj musimy podać któremu obiektowi chcemy to przepisać, a więc pod Person
prototype mamy przecież obiekt, więc mówimy dodaj właściwość
do obiektu Person prototype, która ma się nazywać sayHello i jako
trzeci parametr podajemy tak zwany property descriptor i teraz
tutaj musimy ustawić sobie kilka rzeczy, ale my ustawimy tylko jedno. Jest
to enumerable, enumerable na false, czyli że
ma tu być właściwość niewyliczalna, to nie znaczy, że ona jest ukryta, ale
jest niewyliczalna na przykład w pętli for in. Natomiast teraz musimy podać pod
kluczem value jej wartość, a że to była funkcja, to musimy ją tutaj wkleić, czyli aby
taka właściwość była niewyliczalna, to musimy to zrobić w taki sposób, a nie
w taki jak tutaj. Okej, zobaczmy i teraz ona jest niewyliczalna, aczkolwiek
poprzez Person sayHello
powinno to działać dokładnie tak samo i jak widzisz wynik jest ten sam, więc
zapamiętaj, że jest to jedna z istotnych różnic jeżeli chodzi o zapis klasy i o
zapis taki stary w EcmaScript 6, automatycznie klasy tworzą nam na prototypie takie
metody, które są non enumerable. Kolejną ciekawostką,
którą chciałbym ci pokazać jest ciekawostka dotycząca dziedziczenia. Będę sobie
chciał wkleić metodę, którą mieliśmy
wcześniej czyli employee, w zasadzie klasę i ona
dziedziczyła z Person. To co mamy tutaj może sobie wykomentujmy i odkomentujemy
jeszcze na sekundę ten zapis, żeby upewnić się, że wszystko działa, więc
dziedziczymy sobie z Person i mamy tutaj employee1. Sprawdźmy
co będzie pod employee1 sayHello. Jak
widzisz nazywam się Jan Kowalski, pracuję jako programista. Natomiast okazuje się, że możemy
sobie dziedziczyć nie tylko z klas. Jeżeli nie mielibyśmy tej klasy, ale mielibyśmy
funkcję, czyli stary zapis EcmaScript 5
też będziemy mogli dziedziczyć pisząc extends Person. Zobaczmy.
Spróbuję wywołać to co wcześniej i wszystko działa dokładnie tak samo. Mógłbyś
zapytać teraz po co nam to potrzebne. Otóż okazuje się, że mamy w internecie
sporo takiego kodu, który jest napisany w ES5 i mamy sporo różnych bibliotek i
modułów i dobra wiadomość jest taka, że możemy zacząć pisać ES6 i dziedziczyć
sobie można powiedzieć z tych klas, nie napisanych w syntax'ie klas. Będzie
to działać bez żadnego problemu. Natomiast ważne jest, że możemy dziedziczyć z funkcji.
No to jest przecież funkcja, ale jeżeli nie są to funkcje strzałkowe, czyli gdyby były
to funkcje strzałkowe, wtedy nie możemy zrobić extends. Natomiast z każdej innej
funkcji możemy sobie w taki sposób dziedziczyć, więc jest to ciekawostka nr
2. Ciekawostką numer 3 jest to, że za
każdym razem kiedy tworzymy klasę to wszystko co jest wykonywane wewnątrz tej klasy, czyli kod
w każdej metodzie korzysta z tak zwanego strict mode, czyli tak jak byśmy mieli po
prostu napisane w dokumencie use strict, to wtedy
cały nasz skrypt korzysta z tego strict mode i z nim się wiąże oczywiście szereg różnych
innych zachowań interpretera JavaScript. Natomiast warto pamiętać, że
wewnątrz zapisu klas zawsze będziemy korzystać ze strict mode, a
tutaj wcale by tak nie było. Tak więc nie da się tego wyłączyć i pamiętaj,
że wszystko to co gdzieś usłyszysz, czy przeczytasz na temat strict mode, jak
on się zachowuje w funkcjach i tak dalej, to będzie miał zastosowanie wewnątrz każdej
metody klasy. Kolejną ciekawostką jest fakt, że chociaż klasa pokazywałem
ci, że jeżeli zrobimy type of person i stworzymy za pomocą klas,
to jest to function i funkcje mają na przykład metodę call i apply, z której z resztą
korzystaliśmy w jednej z poprzednich lekcji, aby tutaj w konstruktorze pamiętasz
napisać sobie person call, przekazaliśmy
this. Oczywiście nie byliśmy wtedy w konstruktorze Person, tylko employee. Niemniej jednak Person
kropka call działało. Ważne jest to, że na klasie, chociaż teoretycznie jest
to funkcja i można korzystać z metod call i apply. Kolejna
ciekawostka jest taka, że możemy się odwołać wewnątrz metody w klasie do
czegoś takiego jak new target. Nie jest dostępne nigdzie indziej, ale właśnie
w takich metodach, więc możemy się odwołać do new target i to nam będzie kierowało
na klasę, na konstruktor klasy czy w zasadzie na klasę, z której został
w ten obiekt wywołany. Czyli jeżeli napisałem new Employee, to new
target będzie kierować na employee, ale jeżeli dziedziczymy
sobie z Person, to tutaj wewnątrz new person, może to wykomentujemy, a to
sobie odkomentujmy. Tutaj wewnątrz, jeśli bym się odwołał do new Target, to jeżeli
oczywiście zrobimy new Person to new target będzie
kierować na person, ale w tym przypadku zrobiliśmy new Employee. Wiemy,
że ten konstruktor poprzez super wywołuje ten konstruktor i w tym konstruktorze
wtedy new target będzie kierować na employee i co jest ciekawe, moglibyśmy
sobie teoretycznie utworzyć klasę abstrakcyjną, czego domyślnie nie ma w języku JavaScript,
ale jest w innych i klasa abstrakcyjna działa tak, że chociaż ona definiuje
różne metody czy na przykład właściwości, to nie można z niej tworzyć nowych
obiektów, więc gdybym sobie chciał tutaj utworzyć na przykład var
person1 równa się new Person Jan Kowalski, to
wiemy, że nie masz z tym żadnego problemu. Mogę to
zrobić i widzimy, że nie ma żadnego błędu. Natomiast klasy abstrakcyjne działają
tak, że wyłącznie można z nich dziedziczyć i tworzyć nowe instancje klasy pochodnej, więc
jak byśmy mogli za pomocą new Target sprawić, żeby ta kasa była abstrakcyjna. Otóż
moglibyśmy tutaj już na samym początku sprawdzić sobie
if new Target nie jest równe albo
jest równe w zasadzie person, to oznacza, że ktoś napisał
new Person i wtedy new Target kieruje dokładnie na new Person, to możemy wyrzucić
błąd throw new Error i możemy napisać na
przykład, że klasy Person nie można używać bezpośrednio Coś
w tym stylu moglibyśmy napisać, no i zobaczymy pojawi nam się błąd. Nie
możemy utworzyć instancji klasy person bezpośrednio i teraz
jeżeli chciałbym utworzyć new employee, nie korzystam bezpośrednio
z new Person, ale z klasy która z niej dziedziczy, okaże się, że nie ma żadnego błędu, więc
w ten sposób można by było sobie zasymulować na przykład klasę abstrakcyjną i ostatnia już
ciekawostka. Otóż z klasy możesz korzystać, podobnie jak z funkcji anonimowych
w formie wyrażenia, więc gdybym sobie chciał utworzyć funkcję, którą bym nazwał
powiedzmy createInstance i tutaj by się
przekazywało klasę, czyli na przykład from class, nie mogę nazwać zmiennej samo class,
bo jest to słowo zastrzeżone, ale nazwijmy from class,
a tutaj dalej przekazywałoby się jakieś dane na przykład firstName,
lastName, więc wiesz, że możemy je zabrać operatorem rest, na przykład args, to chciałbym
żeby ta funkcja zwracała nam new from class, czyli
tam musi być jakaś klasa przekazana i chcemy jej operatorem spread przekazać wszystkie argumenty. Mam
nadzieję, że to jest dosyć proste i teraz moglibyśmy sobie napisać na przykład let person2,
to się równa createInstance i tutaj jako wyrażenie
przekazalibyśmy klasę bez nazwy. Do tego dązę, że właśnie klasy
robiliśmy z nazwą. A można napisać całą kasę bez nazwy,
możesz oczywiście ją też przypisać do zmiennej, tutaj przekazać. Ale ja w sposób anonimowy
ją przekazuje w to miejsce i ta klasa powiedzmy miałaby konstruktor, który
by przyjmował firstName, lastName i robimy
to, co już znamy i tutaj ta klasa się kończy,
a następnie przekazujemy parametry, czyli przekazali byśmy sobie na przykład Anna
Nowak. Prześledźmy jeszcze raz jak to
działa. Wywołuje funkcję createInstance i jako pierwszy argument przekazuje jej klasę,
to ona wpadnie tutaj pod from class. Tutaj mamy literówkę.
Natomiast drugi, trzeci i każdy kolejny parametr, będzie w formie tablicy zebrany
pod args i ta funkcja robi to, że wywołuje new from class,
czyli tak naprawdę zrobi new na tej klasie, przekazując jej wszystkie argumenty przekazane
dalej. Zobaczmy. Nie ma błędu i teraz bym się odwołał do person 2, to
zobaczymy taki oto obiekt. To oznacza, że klasy możesz bez nazwy w ten
sposób przypisać do dowolnej zmiennej czy przekazać ją jeszcze inne miejsce. Natomiast ważne
jest to, że ona wtedy nie będzie miała pod właściwością name niczego.
Będzie to pusty ciąg znaków, więc teraz jeżeli ją przekazałem tutaj anonimowo, to
jak się można do niej odwołać. Możemy się odwołać do person2 konstruktor
i to nam zawsze zwraca tą klasę, więc jeśli byśmy sprawdzili, to jej name będzie
miało pustą wartość. No i to by było tyle, jeżeli chodzi o ciekawostki.
Mam nadzieję, że jeszcze kilka tutaj nowości zobaczyłeś i teraz już naprawdę wiesz
jak pracować z klasami w ES6. Jak je pisać, jak można w
jednej klasie dziedziczyć z innej, że można również tworzyć na przykład metody statyczne czy dziedziczyć z
wbudowanych typów takich jak array, a także co jest moim zdaniem szczególnie
ważne do zapamiętania z tej lekcji, że można dziedziczyć sobie również ze
zwykłych funkcji pod warunkiem, że nie są to funkcje strzałkowe.