Blog (30)
Komentarze (5.6k)
Recenzje (0)

ECMAScript 6 wydany!

@mikolaj_sECMAScript 6 wydany!23.06.2015 19:41

Można zrozumieć fakt, że większość ludzi bardziej pasjonuje się informacją o utworzeniu projektu WebAssembly niż tym, że właśnie po długich i żmudnych negocjacjach udało się zatwierdzić nową wersję (już szóstą) standardu ECMAScirpt (17 czerwca). Nie każdy jednak rozumie, że tak szybka zgoda wiodących firm IT, co do wspólnego rozwijania wasm (skrót od WebAssembly), przyszła na fali udanej współpracy przy zatwierdzaniu standardu, będącego podstawą JavaScriptu, jedynego języka działającego po stronie przeglądarek. Są to więc jak najbardziej powiązane ze sobą wątki. Owa zgoda nie przyszła tak łatwo jak to teraz wygląda.

Prace nad ECMAScript 6 ciągnęły się dość długo (wystartowały w 2010r.). Firma Google przez dłuższy czas optując za bardzo głębokimi zmianami, próbowała stworzyć swój własny język Dart niekompatybilny z JavaScript. Tymczasem Microsoft zainwestował w język TypeScript, który to wprowadzając duże zmiany w sposobie programowanie (przede wszystkim typowanie) nie zrywał kompatybilności z masą kodu napisanego w standardowym JavaScript. Mozilla w tym czasie skupiała się na przyspieszaniu kodu działającego w przeglądarce dzięki swojemu ASM.js. Brak zgody mógł mieć bardzo przykre konsekwencje dla całej branży IT związanej z rozwiązaniami WWW. Pojawienie się niekompatybilnego Darta mimo, że umożliwiał on kompilację do JS, stanowiło zagrożenie, że niektóre przeglądarki będą działać inaczej niż pozostałe. Mogłoby się np. okazać, że piszący w Darcie nie zawsze dołączą do strony, oprócz samego skryptu w Dart, wersji skompilowanej do JS. A jednak po stronie Google coś jakby pękło i nastąpiły zmiany. Nagle spodobał im się język TypeScript i całkowita zmiana w standardzie ECMAScript przestała być konieczna. Przy okazji Google zauważyło, że to co robi Mozilla z przyspieszaniem JS przez ASM.js i kompilowaniem do niego kodu z C/C++ ma więcej sensu niż ich walka o NaCl (Native Client), czyli umożliwianiem uruchamiania kodu natywnego w przeglądarce. Moim zdaniem NaCl zbytnio przypomina ActiveX. Łatwo się domyśleć, że uczynienie tego rozwiązania bezpiecznym musi wymagać olbrzymiej pracy, a jej wynik ciągle jest niepewny. Tymczasem technologia WebAssembly może spokojnie zastąpić NaCl, dając większe bezpieczeństwo, niewiele mniejszą wydajność, a dodatkowo pozwalając zachować kompatybilność aplikacji w wielu różnych przeglądarkach.

Koniec prac nad ES6 nie kończy oczywiście drogi rozwoju tego standardu. Już wiadomo nad czym będą się toczyć pracę przy wersjach następnych. Między innymi SIMD, niedawno wprowadzony eksperymentalnie do Firefoxa ma być częścią nowego standardu. Ciekawie wygląda propozycja wprowadzenia opcjonalnego typowania znanego z TypeScriptu. Wszystko wskazuje na to, że dalsze prace dzięki osiągniętemu porozumieniu pójdą dużo szybciej niż to miało miejsce w ostatnich latach. Duży wpływ na to co będzie się działo z ECMAScirpt będzie prawdopodobnie mieć postęp prac nad WASM. Niektórzy wieszczą upadek JavaScriptu i zastąpienie go kompilatorami do WASM innych popularnych języków programowania. IMHO kierunek całkiem możliwy aczkolwiek mało prawdopodobny. Podstawą WebAssembly na początek będzie zapewne ASM.js, a ten opiera się o JavaScript. Zatem długo ten ostatni będzie podstawą tworzenia kodu działającego po stronie klienta. Poza tym język ten jest zbyt popularny, aby miał nagle zostać zastąpiony przez inne. Jednak otwiera się nowa perspektywa dla osób, które chciałyby pisać wszystko w jednym języku (po stronie serwera i przeglądarki) i niekoniecznie miałby to być JavaScript.

Nowości w JavaScript dostępne w Firefoxie

Na większe zmiany musimy jednak jeszcze trochę poczekać, a tymczasem warto zacząć myśleć nad zastosowaniem nowych elementów JavaScriptu, które pojawiły w standardzie o numerze 6. Duża ich część została już wprowadzona przez główne przeglądarki. Tutaj skupię się na Firefoxie. Składnia używa frameworka do testowania Jasmine.

Strzałka funkcji anonimowej

    var multiply = (x, y) => x*y;
    expect(typeof multiply).toEqual("function");
    expect(multiply(11, 12)).toEqual(132);
    expect(multiply(0, 1234)).toEqual(0);
    expect(multiply(0.1, 1.3)).toBeLessThan(0.14);

Dość podobna składnia do innych znanych języków. Przy braku parametrów wstawiamy puste nawiasy.

Łamanie napisów. Tworzenie napisów ze zmiennych.

    var str = `W JavaScript string
    nie łamie linii`;
    var imie = "Jan";
    var nazwisko = "Kowalski";
    var format = `Witaj ${imie}ie ${nazwisko}!`;
    expect(str).toEqual('W JavaScript string\n  nie łamie linii');
    expect(format).toEqual("Witaj Janie Kowalski!");
   expect('abc'.repeat(2)).toEqual('abcabc');

Łamanie linii i scalanie stringów ze zmiennych działa tylko przy użyciu znaku grawis (odwrócony apostrof, tam gdzie tylda ~).

Zbiorcze inicjowanie danych tabelami

    var [x, , , y] = [1, 2, 3, 4]; 
    var [z = 8] = [];
    expect(x).toEqual(1);
    expect(y).toEqual(4);
    expect(z).toEqual(8);

Zmienna z jest domyślnie inicjowana na 8, a ponieważ w tablicy nie ma elementów więc nie ustawimy jej wartości na inną niż domyślna.

Stałe

const pi = 3.14;
expect(Math.round(2*pi*10)).toEqual(63);
//pi = 3.141;  - błąd 

Parametry funkcji

function system(a, b = 10) {
    var str = "";
    while(a != 0){
      str = (a % b) + str;
      a = Math.floor(a / b);
    }
    return str;
  }
  expect(system(128, 2)).toBe("10000000");
  expect(system(1234)).toBe("1234");

Domyślny parametr funkcji. [code=javascript]function tablica(a, ...b) { return a + b.join("-"); } expect(tablica("funkcja:","tablico","zmienna")).toEqual("funkcja:tablico-zmienna"); [/code]

Dowolna ilość parametrów na liście odbierana w funkcji jako tablica.


  function szereg(a, b, c) {
    var q = `${a}, ${b} czy ${c}?`;
    q = q[0].toUpperCase() + q.substring(1, q.length);
    return q;
  }
  expect(szereg(...['kawa', 'herbata', 'woda'])).toBe('Kawa, herbata czy woda?');
 

Zamiana tablicy na listę argumentów.

Kolekcje


    var array = [1, 2, 3, 4];
    array.fill(5);
    expect(array).toEqual([5,5,5,5]);
    array.fill(2,2);
    expect(array).toEqual([5,5,2,2]);
    array.fill(1,1,2);
    expect(array).toEqual([5,1,2,2]);
    var napis = "tab";
    array = [1, 2, 3, 4];
    expect(Array.from(napis)).toEqual(["t", "a", "b"]);
    expect(Array.from(array, x => x*10)).toEqual([10, 20, 30, 40]);

Metoda wypełniania tablicy fill z parametrami (czym wypełnić, od którego miejsca, do którego miejsca). Metoda statyczna from pozwala stworzyć nową tablicę na podstawie argumentu innej kolekcji, którą również możemy wstępnie przetworzyć.

    var m = new Map();
    m.set("0", "pierwszy");
    m.set(1, "drugi");
    m.set({}, "trzeci");
    m.set("obj", {cont:"czwarty"});
    var itMap = m.keys();
    expect(itMap.next().value).toEqual("0");
    expect(itMap.next().value).toEqual(1);
    expect(itMap.next().value).toEqual({});
    expect(itMap.next().value).toEqual("obj");
    itMap = m.values();
    expect(itMap.next().value).toEqual("pierwszy");
    expect(itMap.next().value).toEqual("drugi");
    expect(itMap.next().value).toEqual("trzeci");
    expect(itMap.next().value.cont).toEqual("czwarty");
    expect(m.get("obj").cont).toEqual("czwarty");
    expect(m.has(1)).toBe(true);
    var numbers = new Array();
    m.forEach((v, k) => numbers.push(v));
    expect(numbers).toEqual(["pierwszy", "drugi", "trzeci", {cont:"czwarty"}]);

Mapy umożliwiają przechowywanie na zasadzie klucz-wartość. Zarówno klucz jak i wartość może być dowolnego typu. Możemy pobierać zestaw kluczy (keys()) i wartości (values()). Zwracają one iterator do mapy, przez który przechodząc, mamy możliwość przeglądać elementy.


   var s = new Set();
    s.add(34);
    s.add(34);
    expect(s.size).toBe(1);
    s.add("nowy");
    expect(s.size).toBe(2);
    expect(s.has("nowy")).toBe(true);
    s.delete(34);
    expect(s.size).toBe(1);
    expect(s.has(34)).toBe(false);
    s.clear();
    expect(s.size).toBe(0);
    s.add(10);
    s.add(1);
    s.add(20);
    s.add(5);
    s.add(4);
    s.add(16);
    array = [];
    for(e of s){
      array.push(e);
    }
    expect(array).toEqual([10,1,20,5,4,16]);

Set jest rodzajem tablicy bez powtarzania się tych samych wartości. Dodanie drugiej takiej samej wartości nie powiększa rozmiaru kolekcji. W ostatniej pętli for widzimy nową składnie dla kolekcji, w której zwracany jest kolejny element, a nie jak w wersji z in jego numer.


    var float32 = new Float32Array(2);
    float32[0] = 42;
    expect(float32[0]).toEqual(42);
    expect(float32.length).toBe(2);
    expect(float32.BYTES_PER_ELEMENT).toBe(4);
    var arr = new Float32Array([21,31]);
    expect(arr[1]).toBe(31);
    var x = new Float32Array([21, 31]);
    var y = new Float32Array(x);
    expect(y[0]).toEqual(21); // 21

    var buffer = new ArrayBuffer(16);
    var z = new Float32Array(buffer, 0, 4);
    expect(z.length).toBe(4);
    expect(z[2]).toBe(0);

Istnieje szereg rodzajów tablic pozwalających przechowywać szczególne typy danych prostych. Pomocnicze tablice ArrayBuffer i DataView pozwalają pracować bezpośrednio z danymi binarnymi - wczytywać je i zamieniać na inne typy.

Generator


   function* idMaker(){
      var index = 0;
      while(index < 3)
      yield index++;
   }
    var gen = idMaker();
    expect(gen.next().value).toBe(0);
    expect(gen.next().value).toBe(1);
    expect(gen.next().value).toBe(2);
    expect(gen.next().value).toBe(undefined);

Oznaczone gwiazdką za pomocą słowa kluczowego yield umożliwiają tworzenie kolekcji na bieżąco, bez zajmowania dużej ilości pamięci. W wyniku ich działania dostajemy iterator do przechodzenia przez generowane wartości.


    var ws = new WeakSet();
    var obj1 = {a:1};
    var obj2 = {a:1};
    var obj3 = new Object();
    obj3.a = 8;
    var fun = function(x) {return x*5;}
    ws.add(obj1);
    ws.add(obj2);
    ws.add(obj3);
    ws.add(fun);
    expect(ws.has(obj2)).toBe(true);
    expect(ws.has(obj1)).toBe(true);
    expect(obj1).toEqual(obj2);
    expect(obj1).toEqual({a:1});
    expect(ws.has({a:1})).toBe(false);
    expect(ws.has(fun)).toBe(true);
    ws.delete(obj3);
    expect(ws.has(obj3)).toBe(false);
    var obj5 = {a:67};
    obj5.sub = {b:45};
    ws.add(obj5.sub);
    expect(ws.has(obj5.sub)).toBe(true);
    delete obj5.sub;
    expect(ws.has(obj5.sub)).toBe(false);

Przechowuje tylko unikalne obiekty. Nie przechowuje typów prostych. Jest to przedstawiciel tablicy, która nie blokuje w garbage collector referencji do przechowywanego obiektu. Przez co obiekt ten znajduje się w kolekcji, dopóki istnieje do niego inna referencja. Usunięcie jej powoduje, tak jak widać w końcówce kodu, zniknięcie elementu z tablicy. Nie pozwala ona pobrać wszystkich wartości i nie ma pola

Cechy niedostępne lub dostępne w gałęzi Nighty

Aby sprawdzić składnie nowych elementów nie wspieranych przez Firefoxa dodamy skrypt, który analizuje i podmienia kod JS na stronie. Musi on znajdować się bezpośrednio w stronie w znaczniku (niżej niż skrypty parsujące). Na samym początku body dodajemy skrypty parsujące:

<script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script>
<script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script>

Słowo kluczowe class


class Vektor2D {
        constructor(x, y){
          this.x = x;
          this.y = y;
          this.unit = "";
        }
        len() {
           return Math.sqrt(this.x*this.x + this.y*this.y);
        }
        showLen(){
          document.querySelector('#classInfo').innerHTML = this.len()+ this.unit;
        }
 };
class Force extends Vektor2D {
        constructor(x, y){
          super(x, y);
          this.unit = "N";
        }
 };

var f = new Force(40, 30);
 f.showLen();

Kod zakłada istnienie na stronie akapitu z id classInfo. Jak widać wygląda to na podobną do innych popularnych języków obiektowych definicję klasy z metodami i konstruktorem dwu-parametrowym. Dziedziczenie jest realizowane dzięki słowu kluczowemu extends. We wnętrzu konstruktora Force uruchamiamy konstruktor rodzica używając słowa super. Do pełni szczęścia brakuje tylko opcjonalnego typowania ;)

Zmienna let

Zmienna deklarowana poprzez słowo let zachowuje się, jeśli chodzi o zasięg, podobnie do zmiennych w językach pochodzących od C.


       if(true){
         let tmp1 = "let!";
         var tmp2 = "var!";
       }
         let element = document.querySelector('#var');
         element.innerHTML = tmp2;
         //element = document.querySelector('#let');
         //element.innerHTML = tmp1;

Zmienna var tmp2 mimo, że zadeklarowana w nawiasach ograniczających zwrotnicę if jest widoczna po zamknięciu nawiasu klamrowego. Nie jest to naturalne zachowanie dla większości programistów, przyzwyczajonych do niewidoczności zmiennej po wyjściu bloku kodu. W taki sposób zachowuje się tmp1 zadeklarowana ze słowem let. Obecnie próba odwołania do niej spowoduje błąd naszego parsera.

Brakuje jeszcze szeregu metod w różnych obiektach. Aktualny stan zobaczysz na stronie: developer.mozilla.org

Można też przetestować własną przeglądarkę korzystając z testów napisanych przez firmy pracujące nad nowym standardem (nie ma nic wspólnego z przykładami kodu w tym artykule): LINK W obecnie używanej przeze mnie wersji Firefoxa 38 (Ubuntu 14.04 LTS) nie przeszło 230 z 11552 testów. Ponoć najlepiej wypada pod tym względem IE. (Dla tych co nabrali ochoty do test: może on zająć nawet kilka godzin).

Podsumowanie

Zmian jest sporo i warto się z nimi zapoznać. Wpis nie wyczerpuje ich wszystkich w tym tak ekscytujące jak Promisses, Tail Calls czy Proxies. Z wprowadzaniem ich w życie można spodziewać się jednak opóźnienia. Podejrzewam, że nie prędko do produkcji wejdzie pisanie kodu w EM6. Trudno liczyć na to, że w szybkim czasie wszystkie urządzenia zaczną go obsługiwać. Szczególnie w przeglądarkach mobilnych, które są często archaicznymi tworami sprzed dekady z powodu braku aktualizacji systemu. Pozostaje liczyć na to, że ilość takiego sprzętu będzie na tyle mała, że warto będzie zacząć pisać w nowym dialekcie. Pomóc nam mają dołączane do stron biblioteki, zapewniające kompatybilność dla przeglądarek nie posiadających możliwości interpretacji kodu ECMAScript 6. Niektóre cechy podobają mi się na tyle, że postaram się jak najszybciej zacząć go używać.

PS. Kod przykładów można pobrać z gita: https://github.com/mikolajs/ecmascript6-test.git

Szanowna Użytkowniczko! Szanowny Użytkowniku!
×
Aby dalej móc dostarczać coraz lepsze materiały redakcyjne i udostępniać coraz lepsze usługi, potrzebujemy zgody na dopasowanie treści marketingowych do Twojego zachowania. Twoje dane są u nas bezpieczne, a zgodę możesz wycofać w każdej chwili na podstronie polityka prywatności.

Kliknij "PRZECHODZĘ DO SERWISU" lub na symbol "X" w górnym rogu tej planszy, jeżeli zgadzasz się na przetwarzanie przez Wirtualną Polskę i naszych Zaufanych Partnerów Twoich danych osobowych, zbieranych w ramach korzystania przez Ciebie z usług, portali i serwisów internetowych Wirtualnej Polski (w tym danych zapisywanych w plikach cookies) w celach marketingowych realizowanych na zlecenie naszych Zaufanych Partnerów. Jeśli nie zgadzasz się na przetwarzanie Twoich danych osobowych skorzystaj z ustawień w polityce prywatności. Zgoda jest dobrowolna i możesz ją w dowolnym momencie wycofać zmieniając ustawienia w polityce prywatności (w której znajdziesz odpowiedzi na wszystkie pytania związane z przetwarzaniem Twoich danych osobowych).

Od 25 maja 2018 roku obowiązuje Rozporządzenie Parlamentu Europejskiego i Rady (UE) 2016/679 (określane jako "RODO"). W związku z tym chcielibyśmy poinformować o przetwarzaniu Twoich danych oraz zasadach, na jakich odbywa się to po dniu 25 maja 2018 roku.

Kto będzie administratorem Twoich danych?

Administratorami Twoich danych będzie Wirtualna Polska Media Spółka Akcyjna z siedzibą w Warszawie, oraz pozostałe spółki z grupy Wirtualna Polska, jak również nasi Zaufani Partnerzy, z którymi stale współpracujemy. Szczegółowe informacje dotyczące administratorów znajdują się w polityce prywatności.

O jakich danych mówimy?

Chodzi o dane osobowe, które są zbierane w ramach korzystania przez Ciebie z naszych usług, portali i serwisów internetowych udostępnianych przez Wirtualną Polskę, w tym zapisywanych w plikach cookies, które są instalowane na naszych stronach przez Wirtualną Polskę oraz naszych Zaufanych Partnerów.

Dlaczego chcemy przetwarzać Twoje dane?

Przetwarzamy je dostarczać coraz lepsze materiały redakcyjne, dopasować ich tematykę do Twoich zainteresowań, tworzyć portale i serwisy internetowe, z których będziesz korzystać z przyjemnością, zapewniać większe bezpieczeństwo usług, udoskonalać nasze usługi i maksymalnie dopasować je do Twoich zainteresowań, pokazywać reklamy dopasowane do Twoich potrzeb. Szczegółowe informacje dotyczące celów przetwarzania Twoich danych znajdują się w polityce prywatności.

Komu możemy przekazać dane?

Twoje dane możemy przekazywać podmiotom przetwarzającym je na nasze zlecenie oraz podmiotom uprawnionym do uzyskania danych na podstawie obowiązującego prawa – oczywiście tylko, gdy wystąpią z żądaniem w oparciu o stosowną podstawę prawną.

Jakie masz prawa w stosunku do Twoich danych?

Masz prawo żądania dostępu, sprostowania, usunięcia lub ograniczenia przetwarzania danych. Możesz wycofać zgodę na przetwarzanie, zgłosić sprzeciw oraz skorzystać z innych praw wymienionych szczegółowo w polityce prywatności.

Jakie są podstawy prawne przetwarzania Twoich danych?

Podstawą prawną przetwarzania Twoich danych w celu świadczenia usług jest niezbędność do wykonania umów o ich świadczenie (tymi umowami są zazwyczaj regulaminy). Podstawą prawną przetwarzania danych w celu pomiarów statystycznych i marketingu własnego administratorów jest tzw. uzasadniony interes administratora. Przetwarzanie Twoich danych w celach marketingowych realizowanych przez Wirtualną Polskę na zlecenie Zaufanych Partnerów i bezpośrednio przez Zaufanych Partnerów będzie odbywać się na podstawie Twojej dobrowolnej zgody.