Przegląd ECMAScript 6


25 lutego 2015 / Michał Załęcki


ECMAScript 6 jest nową choć nie najnowszą (ECMAScript 7), wersją standardu ECMAScript i bazuje na ECMAScript 5.1 z 2009 r.

Od lipca 2014, ES6 nie jest poszerzany o nowe funkcjonalności. Po procesie publikacji, który rozpocznie się w marcu 2015, ES6 będzie ukończony w czerwcu 2015.

Obecnie nie ma dobrych powodów, dla których nie mielibyśmy zacząć używać ES6 już dziś. Jest to możliwe dzięki takim narzędziom jak Traceur od Google czy Babel (6to5.js).

W tym artykule mam zamiar pokazać Ci niektóre funkcjonalności ECMAScript 6 bazując na jednym z moich repozytoriów gdzie znajdziesz więcej przykładów. Nie odkryję Ameryki jeżeli stwierdzę, że najlepszą metodą na uczenie się nowych narzędzi (bibliotek, frameworków, języka itd.) jest pisanie testów i takiego podejścia staram się trzymać. Jest to główny powód dla którego kod w repozytorium nie jest tylko zbiorem przykładów, a również mini środowiskiem do transpilacji ES6 do ES5 (poprzez Traceur) oraz testów jednostkowych (Karma, Jasmine).

Pomimo tego, że Traceur to wspaniałe narzędzie to ma ono swoje ograniczenia np. brak obsługi Proxy. Jeżeli szukasz dokładniejszych informacji odnośnie tego jakie narzędzia wspierają jakie funkcjonalności dobrym punktem wyjścia będzie ECMAScript compatibility table.

Zwróć uwagę na to, że „ostateczne wsparcie” twojego środowiska jest sumą funkcjonalności wspieranych przez twoją przeglądarkę lub serwer oraz transpilator z jakiego korzystasz (jeżeli w ogóle). Przykładowo Traceur nie wspiera struktury WeekMap, ale Chrome radzi sobie z jej obsługą całkiem nieźle co oznacza, że z powodzeniem możesz wykorzystywać WeekMap w swoim kodzie. Ja przy tworzeniu przykładów korzystałem z Chrome i jeżeli również chcesz z niego korzystać nie zapomnij o przełączeniu flagi odpowiedzialnej za włączenie eksperymentalnych funkcjonalności JavaScript.

Let

Słowo kluczowe let jest podobne do dobrego, starego var. Różnica leży w zasięgu. Zasięg var jest globalny lub ograniczony poprzez funkcję co bez posiadania tej wiedzy może prowadzić do nieoczekiwanych bugów (np. zmienna stworzona w bloku instrukcji if będzie dostępna również poza nią). Let natomiast posiada zasięg ograniczony przez blok lub wyrażenie co, dla osób, które lepiej czują się w językach statycznie typowanych tj. Java, jest bardziej naturalnym zachowaniem.

Stałe

Stałe jak zapewne się domyślasz nie mogą zostać nadpisane, ale mogą być modyfikowane – można edytować właściwości obiektu przypisanego do stałej. Jeżeli chodzi o zasięg to, podobnie jak w przypadku let, jest ograniczony do bloku czy wyrażenia.

Arrow functions

Arrow functions (funkcje strzałki? bleh.) są skróconym zapisem anonimowej funkcji.

Jeżeli znasz CoffeeScript to arrow functions działają tak samo jak funkcje stworzone za pomocą flat arrow. Poza minimalistyczną składnią arrow functions w odróżnieniu od funkcji stworzonej za pomocą słowa kluczowego function dzielą to samo this z zawierającym je kodem (nie ograniczają zasięgu).

Domyślne parametry

Domyślne parametry pozwalają na przypisanie wartości w przypadku nie podania argumentu uwalniając nas od trochę podstępnego parameter = parameter || default.

Klasy

Czy Ci się to podoba czy nie (jak to jest w moim przypadku), klasy wprowadzone przez ES6, są tylko składniowym cukrem, którym możemy posypać prototypy. W ES6 specyfikacja odnośnie klasy jest naprawdę skromna i nie daje powodów (i możliwości) na porzucenie myślenia obiektowego w JavaScript w kategorii prototypów, przysłaniania właściwości itd. Jednak przyznaję, że extends wygląda przyjaźniej niż Child.prototype = new Parent().

Destructing Assignement

Destructing assignement pozwalają na „wyciągnięcie” konkretnych wartości z tablicy lub obiektu i zapisanie ich do osobnych zmiennych.

Rest Parameters

Rest parameters jest popularnym wzorcem w wielu językach np. RUby, PHP (5.6+). Możemy przyjąć w funkcji wiele parametrów jako tablicę i pożegnać obiekt arguments.

Spread

Operator spread jest odwrotnością rest parameters. Pozwala nam rozbić tablicę na kolejne parametry funkcji.

Symbole

Symbole to nowy, niemutowalny i unikalny typ. Nie są one ani obiektem ani typem prostym. Mogą być wykorzystane jako identyfikatory właściwości obiektów, w tym w WeakMap. Opcjonalna nazwa może być wykorzystana w celu debugowania i przez metodę Symbol.for

Może się wydawać (mi tak się wydawało), że symbole są świetnym sposobem na symulowanie prywatnych właściwości (przez unikalne identyfikatory, niedostępne poza funkcją konstruktora). I wiecie co? Nie są.

Istnieje również coś takiego jak dobrze znane symbole, które mogą być użyte do świetnych rzeczy tj. iteratory. O iteratorach porozmawiamy za chwilę.

Rozszerzone literały obiektu

O ile sam nagłówek pewnie jest niezrozumiały o tyle kryje się pod tym prosta koncepcja. Właściwości obiektu mogą zostać wyliczone, skrócone lub dać dostęp do __proto__. Również zyskujemy możliwość wywołania przysłanianej metody z prototypu ( super()) jednak nie jest to możliwe przy wykorzystaniu Traceur i Chrome 40 (aktualnej wersji w momencie gdy po piszę).

Iteratory

Iterator to obiekty zwracające wartości, do których dostęp możemy uzyskać m. in. za pomocą pętli for..of. Iteratory są deklarowane z wykorzystaniem Symbol.iterator i powinny zwracać obiekt zawierający metodę next. Iteratory są podobne do generatorów jednak jawnie zwracają obiekt, który zawiera dwie właściwości. Właściwość done określa czy iteracja powinna zostać zakończona, a właściwość value to wartość jaka zostanie nadana zmiennej podczas iteracji.

Generatory

Generatory to bardzo potężne narzędzie w rękach programisty. Generatory są deklarowane bardzo podobnie jak zwyczajne funkcje z tą różnicą, że używamy słowa kluczowego function*, a sam generator zamiast zwracać jawnie wartość ( return), będzie podawać wartość więcej niż raz za pomocą  yield.

Istnieje również yield* który powinien „podać” inny generator.

Nie napisałem jeszcze, że metoda next przyjmuje argument, który zostanie zwrócony przez yield.

Ten artykuł jest przeglądem wielu funkcjonalności ES6, wiec nie ma w nim miejsca na szczegółowe zgłębianie każdej z nich. Jeżeli chcesz wiedzieć więcej o generatorach to sprawdź serię wpisów o generatorach w ES6 autorstwa Davida Walsha Kylea Simpsona.

Literały Numeryczne

Dzięki numerycznym literałom praca z liczbami binarnymi i w zapisie ósemkowym staje się banalna.

Literały Szablonów

Literały Szablonów, o których mowa nie mają nic wspólnego web components. Konstrukcja będąca w wielu językach od dawna do JavaScriptu trafia w 2015 roku. Jak to mówią, lepiej późno niż wcale. Wykorzystując trochę inny, bo za pomocą

Co więcej literał taki może otrzymać tag, który jest funkcją, a ta może go zmodyfikować.

Promises

Promises wykorzystywane przez wszystkich na długo przed przyklepaniem specyfikacji i są (w sumie to były) dużym krokiem naprzód w kwestii obsługi asynchronicznych zadań szczególnie gdy te zależały od siebie (pobierz klucz, pobierz token, użyj tokenu itd.). Istnieje wiele implementacji tego wzorca np. $q w AngularJS, który bazuje na Q Krisa Kowala czy rsvp.js.

Więcej przykładów działania promises znajdziesz w promices.js w repozytorium.

Modules

Moduły pozwalają na wygodne korzystanie z zależności dzięki asynchronicznemu ładowaniu i jawnemu eksportowi. Możemy wtedy załadować cały moduł albo zaimportować wybrane eksporty.

Ładowanie modułów działa zarówno po stronie serwera (to raczej nie jest zaskoczenie) jak i po stronie klienta.

Jeżeli interesują cię moduły to warto zainteresować się RequireJS,Browserify i jspm.io.

Map, Set, WeakMap i WeakSet

Map, Set, WeakMap i WeakSet są nowymi strukturami danych. Ich odpowiedniki znamy dobrze z Javy.

Map jest prostą strukturą z relacją klucz-wartość.

Set przechowuje unikalne wartości dowolnego typu.

WeakMap jest kolekcją par klucz-wartość. Klucze są obiektami, a wartości mogą być dowolnego typu.

WeakSet jest najprostszą strukturą ze wszystkich. Przechowuje luźno powiązane obiekty.



22 odpowiedzi na “Przegląd ECMAScript 6”

  1. Polska języka trudna języka pisze:

    Choć, a nie chodź… Pozdrowienia dla „korekty”

  2. Comandeer pisze:

    >Obecnie nie ma dobrych powodów, dla których nie mielibyśmy zacząć używać ES6 już dziś.
    Jedyne powody do pisania czegoś w ES6 to Proxy, subklasowanie natywnych obiektów oraz string interpolation. Żadnej z tych rzeczy żaden konwerter do ES5 nie potrafi. Jedyna przeglądarka mająca to wszystko to nowy lisek, Chrome brakuje obsługa Proxy. A wszystko skrzętnie poukrywane za flagami (polecam spojrzeć ile flag potrzebuje io.js). Pisanie klas tylko po to, żeby je przerobić na prototypy IMO mija się z celem. A już całkowicie nie rozumiem tworzenia całych języków (atScript), które są supersetami ES6 i… są kompilowane do ES5, żeby działać. Dla mnie bezsens.

    Przy stałych i let pominięto najważniejsze rzeczy, czyli 'temporal dead zone’ oraz brak hoistingu. Dzięki temu mamy de facto całkowicie dwa odmienne obiegi zmiennych, które zachowują się całkowicie inaczej.

    >Stałe jak zapewne się domyślasz nie mogą zostać nadpisane, ale mogą być modyfikowane – można edytować właściwości obiektu przypisanego do stałej.
    Wypadałoby przy tym zaznaczyć, że po prostu wynika to z tego, że w JS wartości prymitywne są niemutowalne (zatem porównywane przez wartość), a obiekty posiadają tzw. tożsamość. Nie modyfikujemy samej tożsamości obiektu, jedynie modyfikujemy jego własności.

    Przy arrow functions wypada zwrócić uwagę, że nie dziedziczą po Function.prototype (a przynajmniej nie w pełni)

    >trochę podstępnego parameter = parameter || default
    Czemu podstępnego? Jak ktoś nie zna zasad koercji typów w JS, to nie powinien w tym języku pisać ;)

    >Czy Ci się to podoba czy nie (jak to jest w moim przyp adku), klasy wprowadzone przez ES6, są tylko składniowym cukrem, którym możemy posypać prototypy.
    Czyli mieli w wersji 6 przepisać fundamenty języka tylko dlatego, żeby wprowadzić klasy? IMO wgl ta składnia nie powinna zostać wprowadzona. Większość devów JS nie ogarnia prototypów, a jak się to przysłoni takim lukrem, to znajomość JS spadnie diametralnie. Wystarczy popatrzeć na ludzi, którzy znają jedynie CS…

    >Jednak przyznaję, że extends wygląda przyjaźniej niż Child.prototype = new Parent()
    Akurat to jest najgorszy przykład dziedziczenia w JS. Od dawna lepiej to machnąć przy pomocy Object.create. Z tym, że obydwa sposoby nam pokażą, że Child i tak nie jest potomkiem Parent.

    W sumie jedyna sensowna przewaga extends (oprócz pokazywania poprawnych związków między „klasami”) to możliwość subklasowania natywnych „klas”. I to jest jedyny powód, dla którego sięgnąłbym po klasy.

    >Możemy przyjąć w funkcji wiele parametrów jako tablica i pożegnać obiekt arguments.
    tablicę*

    >Mogą być wykożystane jako identyfikatory właściwości obiektów, w tym w WeakMap.
    wykorzystane*

    >Może się wydawać (mi tak się wydawało), że symbole są świetnym sposobem na symulowanie prywatnych właściwości (przez unikalne identyfikatory, niedostępne poza funkcją konstruktora). I wiecie co? Nie są.
    One mają być po prostu unikalne. IMO Symbol to zbędny bajer i niepotrzebnie wprowadza aż tyle zmian do języka.

    Co do iteratorów i generatorów: warto zwrócić uwagę na to, że generator de facto zwraca iterator.

    Generatory jednak służą przede wszystkim do zarządzania asynchronicznym kodem i w połączeniu z Promises dają niesamowite możliwości, pozostawiając kod czysty i praktycznie „synchroniczny” (zresztą pisałem o tym na tym portalu z 2 lata temu).

    >Jeżeli chcesz wiedzieć więcej o generatorach to sprawdź serię wpisów o generatorach w ES6autorstwa Davida Walsha.
    One nie są autorstwa Davida Walsha, tylko Kyle’a Simpsona. Ja od siebie natomiast polecę blog 2ality.com – jego autor jest prawdziwym mistrzem jeśli chodzi o JS.

    Co do literałów: śmieszne jest to zwłaszcza wówczas, gdy uświadomimy sobie, że literały takie zdeprecjonowano w ES5…

    >Literały Szablonów
    W życiu bym tak tego nie przetłumaczył. W ogóle to jest bolączka naszego community, że próbujemy tłumaczyć wszystko, nie podając równocześnie angielskich terminów. Jak ktoś przeczyta ten artykuł i zechce poszukać czegoś więcej na temat tego ficzera z ES6, to w życiu nie znajdzie tego, czego szuka. Template strings – tyle.

    >Co więcej literał taki może otrzymać tag, który jest funkcją, a ta może go zmodyfikować.
    Bez tego ten dodatek nie miałby najmniejszego sensu. Dzięki temu można było przepisać JSX na JS, pozbywając się całkowicie bzdurnej składni a’la E4X.

    Przy Promises brakuje linku do specyfikacji: https://promisesaplus.com/

    Co do modułów – TC-39 strzeliło sobie w stopę nie opierając składni modułów na tym zaproponowanym przez CJS. Co więcej – obecna implementacja loaderów IMO robi więcej szkody niż pożytku. Nie wspominając o nowym tagu module. Dlatego przez jeszcze bardzo długi czas standardem pozostanie UMD.

    Bardzo mi brakuje opisu Reflection API.

    • Na wiele, rzeczy mogę odpowiedzieć krótko: „PRZEGLĄD ECMAScript 6”, na pisanie dokumentacji miałoby wątpliwy sens – już takie istnieją.

      > Czyli mieli w wersji 6 przepisać fundamenty języka tylko dlatego, żeby wprowadzić klasy?
      Im dłużej piszę w JS to patrząc na niego przez pryzmat np. Ruby to widzę, że przydałyby się mu solidne usprawnienia. Absurdy jak typeof NaN == „number” pomijam. Żeby to zrozumieć trzeba wiedzieć, że NaN jest liczbą przechowywaną w pamięci jako same jedynki – czy taka wiedza powinna być konieczna do zrozumienia sprawdzania typu? Moim zdaniem nie powinna być. Klasy mogłyby być dodane, (a nie zamiast) mimo, że prototypy mają więcej możliwości to widząc class w CoffeeScript cieszyłem się jak małe dziecko i gdybym był w tym odosobniony to CS nie zagościł by domyślnie w Railsach.

      > Akurat to jest najgorszy przykład dziedziczenia w JS.
      Jest wiele sposobów, nie ma „jednego poprawnego”. Jak mam obiekt i chce mu nadać 10 właściwości, które będą enumerowalne i własne to nie użyję Object.create, bo przy każdej muszę napisać jeszcze deskryptor.

      > W życiu bym tak tego nie przetłumaczył.
      Tłumacząc swój artykuł z angielskiego zdecydowałem, że przetłumaczę. Napisz własny i nie przetłumacz :P

      > Co do iteratorów i generatorów: warto zwrócić uwagę na to, że generator de facto zwraca iterator.
      Myślę, że dość jasno wynika to z pierwszego przykładu.

      > >Możemy przyjąć w funkcji wiele parametrów jako tablica i pożegnać obiekt arguments.
      > tablicę*
      Widzę, że nawet taki JSowy maniak jak Ty się dał na to złapać. OBIEKT arguments jest „array like”, nie jest tablicą. Nie posiada np. metody pop by go nie modyfikować. Żeby uzyskać tablice potrzebujesz tego:
      var args = Array.prototype.slice.call(arguments);
      https://developer.mozilla.org/pl/docs/Web/JavaScript/Referencje/Funkcje/arguments

      > Bardzo mi brakuje opisu Reflect API.
      Tak jak pisałem, to jest przegląd. Nowe API obiektu niedługo będzie w repozytorium, na Reflect API też przyjdzie czas.

      • Comandeer pisze:

        >Absurdy jak typeof NaN == „number” pomijam.
        Akurat od tego jest funkcja isNaN, więc taka wiedza nie jest potrzebna. Poza tym raczej nie korzysta się z koercji typów w JS.

        >gdybym był w tym odosobniony to CS nie zagościłby domyślnie w Railsach
        No bo niestety nikt nie kapuje prototypów. To tutaj leży problem, nie w ich niedorobioności

        >Jak mam obiekt i chce mu nadać 10 właściwości, które będą enumerowalne i własne to nie użyję Object.create, bo przy każdej muszę napisać jeszcze deskryptor
        Ale przecież dodatkowe własności można nadać poza Object.create. to Object.defineProperty wymaga oznaczania enumerable w sposób dosłowny, nie Object.create (nie pierwszy parametr).

        >Napisz własny i nie przetłumacz :P
        Ok ;)

        >Widzę, że nawet taki JSowy maniak jak Ty się dał na to złapać. OBIEKT arguments jest „array like”, nie jest tablicą.
        Fajnie, ale ja poprawiałem „jako tablica” :D Poprawna forma to „jako tablicę”. No i ja tam wolę używać Array.from z polyfillem ;)

        >Chętnie zaakceptuję jakieś pull requesty :D
        A zobaczę. Na razie zajmuję się własnym artkiem o ES7 ;)

      • > Akurat od tego jest funkcja isNaN, więc taka wiedza nie jest potrzebna. Poza tym raczej nie korzysta się z koercji typów w JS.
        Jasne, podobnie sprawa wygląda z Array.isArray. Chodzi o to, że jest to dziwne zachowanie – sytuacja nazbyt częsta w JS. Takich przykładów można mnożyć.

        > No bo niestety nikt nie kapuje prototypów.
        Douglas Crockford dobrze to tłumaczy – dużo osób go słucha i czyta. Myślę, że nie jest tak źle.

        > Object.create (nie pierwszy parametr).
        Object.create jest wygodny dla pewnych przypadków, a nie zawsze.

        var obj = Object.create({foo: 1}, {bar: {value: 2}, baz: {value: 3, enumerable: true}});
        var obj2 = new (function () { this.foo = 1; this.bar = 2, this.baz = 3 });
        Object.keys(obj) // [„baz”]
        Object.keys(obj.__proto__) // [„foo”]
        Object.keys(obj2) // [„foo”, „bar”, „baz”] – pożądany wynik

        Pierwszy parametr i tak nie jest ownProperty, pisałem „enumerowalne i własne”. Żeby z obj2 otrzymać taki sam wynik musiałbym jawnie napisać enumerable: true. Jestem daleki od tego, że jedna metoda jest lepsza od drugiej. Lepiej znać dwie i wybrać odpowiednią w danym momencie.

      • Comandeer pisze:

        >Chodzi o to, że jest to dziwne zachowanie – sytuacja nazbyt częsta w JS.
        Pogadajmy o PHP ;) każdy język jest dziwny. Po prostu na inny sposób.

        >Myślę, że nie jest tak źle.
        Masz szczęście w takim razie ;) ja widziałem już wystarczająco dużo.

        >Pierwszy parametr i tak nie jest ownProperty, pisałem „enumerowalne i własne”
        Ok, ale dziedziczy się prototypy. ownProperty to już broszka konstruktora, nie dziedziczenia.
        Ale fakt – warto znać różne.

      • PHP wbrew pozorom nie jest złe. Gorzej sprawa wygląd już ze spójnością API :D

      • Comandeer pisze:

        No popatrz – to jak z JS i DOM ;)
        + taka mała ciekawostka: http://ideone.com/wQA9A2

  3. winek pisze:

    Circle extends Point to nie jest dobry przykład dziedziczenia. Koło nie jest punktem – koło jest zbiorem punktów opisanym przez współrzędne środka i promień. Zatem środek powinien być – podobnie jak promień – właściwością obiektu Circle.

    „Klasy” w ES6 to cukier składniowy na prototypy, który pozwala tworzyć krótszy i bardziej czytelny kod – nic więcej. Jeśli programista nie zna narzędzia, którego używa, to jego problem. Nie widzę powodów do narzekania.

  4. robert pisze:

    fajny artykuł.
    mały błąd jaki znalazłem:
    zamiast „destructing” powinno być „destructuring”

  5. Dzięki za dużą porcję nowości z ES6. Patrząc czasami na składnię w powyższych przykładach zadawałem sobie pytanie, czy to nadal JS :)

  6. Dawid Mazur pisze:

    Dzięki za ten post. Piszę teraz pierwszy projekt z użyciem ES6 i korzystam z twojego artykułu jako takiej „bazy wypadowej” póki nie przyswoję sobie wszystkich nowości i ich używanie nie będzie dla mnie naturalne. Dobra robota!

Skomentuj Dawid Mazur Anuluj pisanie odpowiedzi

Twój adres e-mail nie zostanie opublikowany.