Kurs ES6 od Strefy Kursów

Trochę nieco ponad miesiąc temu Strefa Kursów ogłosiła, że przygotowała kurs o najnowszej wersji JS – ES6. Dawno żadnego kursu nie recenzowałem, więc była to idealna okazja, żeby jakiemuś się przyjrzeć.

Żeby nie było: kurs w całości ukończyłem i zdobyłem certyfikat! Dlatego też mogę z czystym sumieniem poopowiadać, co zmienił w moim życiu.

  • Kurs jest podzielony na 13 rozdziałów, niemniej wydaje mi się, że dwunasty i trzynasty były już lekko dopychane kolanem. Trzynasty równie dobrze można było zmieścić w jednej lekcji, tak samo dwunasty.
  • Po każdym rozdziale znajdował się quiz, niemniej jego jakość pozostawiała ciut do życzenia:

    Pytanie: Do jest potrzebne do uruchomienia narzędzia Babel w lokalnym środowisku?

  • Sam sposób prowadzenia jak najbardziej na plus. Prowadzącego bardzo miło się słuchało. Niemniej zwroty, takie jak duża ilość dostępnych […] czy też wszędobylskie funkcjonalności (w pewnym zdaniu powtórzone chyba z dobrych osiem razy…), drażnią. Na usprawiedliwienie mogę chyba powiedzieć jedynie to, że to jedne z najczęstszych błędów w języku polskim.
  • Gdybym miał ocenić ten kurs ogólnie, to muszę przyznać, że merytorycznie jest na dość wysokim poziomie. Niemniej wszystkie przykłady kodu są nieprawdopodobnie suche (co wyjdzie na przykładach poniżej). Dodatkowo bardzo często do nienagannego merytorycznie materiału nagle dopowiadany był szczegół, który psuł całe wrażenie. I właśnie wydaje mi się, że akurat w tym kursie diabeł naprawdę tkwi w szczegółach.

    Brakowało mi też niezmiernie odpowiedzi na pytanie dlaczego?. Kurs niemal w całości skupił się na odpowiedzi na pytanie jak?, pomijając odpowiedzi na dość istotne pytania. Przez to wiele rzeczy – mimo że zostało omówione ich działanie – zostało potraktowanych po macoszemu.

  • Na samym wstępie, w pierwszym filmiku, pada stwierdzenie, że ES6 to najnowszy standard języka JavaScript (zresztą podobnym hasłem sam kurs jest reklamowany). Nie jest to prawdą. ES6, a dokładniej ES2015, jak sama nazwa wskazuje, jest wersją standardu z roku 2015. W tym też roku ECMA International stwierdziło, że nowe wersje standardu będą wychodzić co roku. Jak nietrudno się domyślić, mamy rok 2017, więc po drodze zdarzyło się nam ES2016. Co więcej, mamy koniec lipca 2017 roku, a że ustalono sobie, że nowe wersje standardu będą wychodzić w czerwcu, mamy nawet już ES2017.

    Pomyślałem początkowo, że może kurs powstał dość dawno i dopiero teraz ujrzał światło dzienne. Jednak nie! W ostatnim rozdziale sam prowadzący zaznacza, że mamy rok 2017 i że teraz omówi przyszłe wersje standardu. Po czym przechodzi do omawiania… ES2016 i ES2017. W tym momencie konstatacja, że jak na przyszłe wersje standardu wsparcie jest zadziwiająco dobre, brzmi po prostu groteskowo. Niemniej plus za omówienie tego wszystkiego na podstawie tablicy kompatybilności Kangaksa.

  • W tym wstępie pada też stwierdzenie, że ES6 wprowadza klasy, które niejako zastępują prototypy i upodobniają JS do innych klasycznych języków obiektowych. Mam z tym twierdzeniem dwa problemy.

    Pierwszy z nich jest natury czysto merytorycznej. Twierdzenie, że klasy są w jakikolwiek sposób alternatywą dla prototypów, jest bardzo sporym nadużyciem. Klasy są niczym innym, jak cukrem składniowym na prototypy. Jedyna różnica zachodzi w kwestii dziedziczenia. Klasy umożliwiają bowiem poprawne dziedziczenie po klasach wbudowanych (np. Promise, Array), prototypy – nie:

    // prototypy
    function MyArray() {
    	Array.call( this, arguments );
    }
    
    MyArray.prototype = Object.create( Array.prototype );
    
    const myArray = new MyArray();
    console.log( myArray instanceof Array ); // true
    
    myArray[ 0 ] = 1;
    console.log( myArray.length ); // 0 – ups…
    
    // klasy
    class MyArray2 extends Array {}
    
    const myArray2 = new MyArray2();
    console.log( myArray2 instanceof Array ); // true
    
    myArray2[ 0 ] = 1;
    console.log( myArray2.length ); // 1 – działa!

    Ta różnica nie jest jednak w kursie w ogóle wspomniana – chociaż opisuje bardzo ważny mechanizm ES6: wewnętrzne właściwości i metody obiektów! Mechanizm ten nie jest nigdzie wspomniany, mimo że pośrednio w kursie się z niego korzysta (np. przy odczytywaniu wartości z Promise).

    Drugi problem jest natury teoretycznej: czemu zakładamy, że języki klasowe (PHP, Java…) są prawdziwie obiektowe?

  • To, że klasy zastępują prototypy, zdementowano w rozdziale poświęconym klasom… i na tym się skończyło. Brakowało mi bardzo porównania kodu opartego na klasach z analogicznym kodem na prototypach.

    Pominięto także bardzo ważne konsekwencje, jakie fakt oparcia klas na prototypach niesie. Nie wspomniano na przykład, że klasy w JS są dynamiczne:

    class MyClass {}
    
    const myClass = new MyClass();
    
    console.log( typeof myClass.method ); // undefined
    
    MyClass.prototype.method = function() {};
    
    console.log( typeof myClass.method ); // function

    Ten przykład doskonale pokazuje, jak ostrożnym trzeba być, omawiając klasy w JS. Niemniej odnoszę wrażenie, że prowadzący faktycznie potraktował je jako ekwiwalent klas choćby z Javy, nie zaznaczając nawet, że klasy tak naprawdę są konstruktorami:

    class MyClass {}
    
    console.log( typeof MyClass ); // function
  • W rozdziale z klasami poruszono także kwestię symboli, lecz zrobiono to bardzo po macoszemu. W kursie symbole sprowadzono do roli unikalnych identyfikatorów. Jest to jednak połowa prawdy. Warto bowiem pamiętać o tzw. dobrze znanych symbolach (ang. well-known symbols). Pozwalają one nadpisywać domyślne zachowania określone przez wspomniane wcześniej wewnętrzne własności i metody:

    const obj = {};
    
    console.log( obj.toString() ); // [object Object]
    
    obj[ Symbol.toStringTag ] = 'hublabubla'
    
    console.log( obj.toString() ); // [object hublabubla]

    Niewspomnienie o tych symbolach jest tym dziwniejsze, że jeden z rozdziałów kursu jest poświęcony iteracji, a istnieje symbol do tworzenia iteratorów.

  • Skoro już wspomniałem o iteracji, przejdźmy do rozdziału kursu, który był jej poświęcony. W dużej mierze skupiał się on na pętli for...of, niemniej padło tam bardzo niezręczne sformułowanie, że jest to alternatywa dla pętli for...in. Nie mogę się z tym zgodzić, ponieważ pętle te służą do dwóch różnych celów. Pierwsza z nich iteruje po wartościach, druga – po kluczach obiektu. Niemniej nie jest to najważniejsza różnica. Najważniejsza różnica polega na tym, że for...of używa wewnętrznego mechanizmu iteracji – którego nie da się omówić ani wyjaśnić bez omówienia dobrze znanych symboli. Przez to wyjaśniona została różnica w działaniu różnych rodzajów pętli, niemniej nie zostało wyjaśnione, skąd ta różnica się bierze.

  • A to prowadzi nas do kolejnego problemu: bez sensownego objaśnienia wewnętrznego mechanizmu iteracji opis generatorów staje się jałowy. O wiele lepiej byłoby pokazać generator w kontekście choćby obiektu będącego kolekcją użytkowników (oczywiście normalna implementacja nie powinna usuwać użytkowników z kolekcji):

    const usersCollection = {
    	users: [
    		'Tomek',
    		'Comandeer',
    		'Yeti'
    	],
    
    	*[ Symbol.iterator ]() {
    		while ( this.users.length > 0 ) {
    			yield this.users.shift();
    		}
    	}
    };
    
    for ( const user of usersCollection ) {
    	console.log( user );
    }

    Przy generatorach wspomniano również, że są podstawą asynchronicznego kodu, niemniej nie pociągnięto dalej tego wątku. A szkoda.

  • Wspomniano także, że WeakMap opiera się na założeniu, że przeglądarka w każdej chwili może z tego obiektu usunąć referencje do już niewykorzystywanych więcej obiektów. Niemniej nie zostało to pokazane na żywo. A przecież Chrome umożliwia ręczne usuwanie śmieci (przycisk kosza w zakładce Memory devtoolsów).
  • Jeśli chodzi o zmienne, to wszystko było w miarę OK. Niemniej pojawiły się pewne istotne szczegóły, które nie dają mi spokoju.

    Zacznijmy od faktu, że można było wspomnieć o temporal dead zone. Jest to bowiem niejako odpowiednik hoistingu przy zmiennych let i const. Nie jest to jednak mega istotny szczegół.

    Takim szczegółem jest natomiast omówienie dokładnej różnicy między let i const i pochylenie się nad tym, dlaczego większość zbiorów dobrych praktyk poleca korzystanie z const zawsze, gdy tylko się da. Co więcej: skoro już wytłumaczyliśmy, w jaki sposób – lepszy od var! – deklarować zmienne w JS, to trzymajmy się tego. Jakież było moje zdziwienie, gdy w kolejnym rozdziale, poświęconym destructuringowi, prowadzący powrócił do var, bo… w sumie nie ma to znaczenia. Skoro nie ma, to dlaczego w ES6 wprowadzono nowy sposób deklarowania zmiennych? Trochę konsekwencji!

    Niemniej najpoważniejszym błędem było stwierdzenie, że hoisting jest blokowy. Otóż nie, hoisting jest funkcyjny:

    // funkcja vs blok
    ( function() {
    	console.log( x ); // undefined – zatem zmienna x została wywindowana aż tutaj
    
    	{
    		var x = 1;
    	}
    }() );
    
    // funkcja vs funkcja
    ( function() {
    	console.log( x ); // ReferenceError – funkcja niżej zablokowała hoisting
    
    	( function() {
    		var x = 1;
    	}() );
    }() );
  • Przejdźmy może do funkcji strzałkowych. Prowadzący wychodzi bowiem z założenia, że są to funkcje takie same jak tradycyjne, ale… Wydaje mi się, że wyjście od innego założenia (w ES6 mamy nowy sposób definiowania funkcji) byłoby logiczniejsze.

    W tym rozdziale pada też bardzo ciekawy przykład, który nie został dokładnie skomentowany:

    let product = () => { id: 12345 }

    Oczywiste jest, że chcemy zwrócić obiekt. Niemniej funkcja ta nie zwraca nic. Prowadzący stwierdził, że w tym wypadku ten zapis jest traktowany jako ciało funkcji. To prawda, niemniej nie zostało wyjaśnione, czemu w takim razie kod, który na pierwszy rzut oka zawiera błąd składniowy, wykonuje się poprawnie. Spójrzmy zatem na nieco inny przykład:

    function test() {
    	id: 12356;
    
    	return 1;
    }
    
    test(); // 1

    JavaScript posiada bardzo mało znany mechanizm etykietowania instrukcji. Przed niemal każdą instrukcją w JS-ie możemy umieścić taką etykietę – mimo że na dobrą sprawę da się ich użyć wyłącznie w pętlach.

  • Poruszono także temat transpilacji, wspomniano nawet martwego Traceura. Niemniej mam wrażenie, że temat potraktowano nieco po macoszemu. Z góry bowiem założono, że będziemy pracować wyłącznie z kodem ES6 i że zawsze będziemy go transpilować w całości do ES5. Nie miałbym do tego zastrzeżeń, gdyby nie istniał babel-preset-env. To małe narzędzie pozwala nam bowiem dostosowywać wynik transpilacji do środowisk, które faktycznie wspieramy.

    Dziwi mnie także stwierdzenie, że kod wypluty przez Babela jest traktowany jako „normalny” kod ES5. Otóż nie, nie jest. To, co wypluwa Babel, na dobrą sprawę jest kodem maszynowym.

  • Przy okazji poruszono także temat polyfills. I tutaj bardzo brakowało mi wspomnienia o usłudze polyfill.io, która pozwala nam zapomnieć o problemie. Dziwi mnie także, że zaprezentowany jako przykład polyfill został ściągnięty ręcznie z GitHuba zamiast zostać zainstalowany z poziomu npm.

  • Na chwilę warto się także zatrzymać przy rozdziale o modułach, gdzie prowadzący stwierdza, że HTTP/2 rozwiązuje wiele bolączek wydajności modułów. To prawda, ale brak realnych testów oraz pchanie całego kodu aplikacji naraz, bez wspomnienia o takich kwestiach, jak server push czy Service Worker, nie wyczerpuje tematu w żaden sposób. Ba, jest to ledwie liznięcie go.

  • Nie rozumiem także, po co były osobne lekcje dotyczące Proxy, Reflect i Typed Arrays. Są to na tyle niszowe rzeczy, że tłumaczenie ich bez sensownych, praktycznych przykładów mija się z celem. Owszem, prowadzący mówi, że Typed Arrays służą do obsługi danych binarnych… ale wszystkie przykłady sprowadzają się do prostych tablic liczbowych.
  • Zdarzały się też drobniejsze potknięcia, jak na przykład stwierdzenie, że Math.sign zwraca 3 wartości. W rzeczywistości zwraca 4 (bo nie można zapomnieć, że oprócz 0 w JS istnieje też -0!). Niemniej myślę, że nie warto się już nad tym rozwodzić.

Prawdę mówiąc nie wiem, jak mam ocenić ten kurs. Z jednej strony poziom merytoryczny był naprawdę w porządku, z drugiej strony – często rzucone ot tak zdanie niweczyło cały, dobry opis danej części ES6. Dodatkowo cały czas miałem wrażenie, że przykłady są niesamowicie teoretyczne, co uderzało najmocniej w przypadku opisu Typed Arrays. Wydaje mi się, że kurs zyskałby dużo, gdyby zamiast tak mocno na teorii, skupiał się też w dużej mierze na praktyce.

10 myśli na temat “Kurs ES6 od Strefy Kursów”

  1. A kojarzysz jakiś ciekawy kurs ES 6+, który możesz polecić? Chciałbym jakoś uporządkować strzępki wiedzy zdobytej tu i ówdzie.

        1. Kurs Piotra Palarza na Eduwebie wydaje mi się bardzo dobry. Jeśli znalazłbyś w nim coś nie do końca poprawnego, byłbym zdziwiony 😉

          Oglądam właśnie kurs z Reacta na szkolenia.nafrontendzie.pl … Nie jest zły, ale jest tu trochę rzeczy, które można by poprawić. Również jezykowo. Aż się prosi o krytykę 🙂

  2. Mój chłopcze, JavaScript nie posiada mechanizmu etykietowania wyrażeń, wyrażenie (expression) 12345 jest częścią instrukcji (statement) i etykieta odnosi się do instrukcji.

    1. Nie jestem Twoim chłopcem, więc możesz sobie darować ten protekcjonalny ton.

      Faktycznie, jest to mechanizm etykietowania instrukcji, nie wyrażeń. W tym jednak przykładzie mamy do czynienia z tzw. expression statement – wyrażeniem, które jest równocześnie instrukcją.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *