Dziś wpis, który nie tak dawno temu zapowiadałem już na Facebooku - na temat ECMAScript 6 prowadziłem ostatnio Lightning Talka (krótka prezentacja, maks 15 minut) u mnie w firmie i pomyślałem, że szkoda by było zmarnować zebrany materiał. Dlatego też dziś wersja blogowa prezentacji zatytułowanej “ECMAScript 6 - co nowego”.

Na początek, dla porządku warto wspomnieć (w razie jakby ktoś nie wiedział), że ECMAScript to standard obiektowego języka programowania, którego jedną z implementacji jest właśnie JavaScript. Obecnie jest to mniej lub bardziej wierna adaptacja wersji 5 tego standardu. Dla przykładu dodam, że innym znanym językiem programowania opartym na ECMAScript jest choćby ActionScript będący częścią technologii Flash firmy Macromedia - młodsi czytelnicy mogą nie pamiętać ale jeszcze parę lat temu Flash był jednym z liderów jeśli chodzi o tworzenie stron internetowych.

Od wydania wersji 5 minęło już sporo czasu, w czasie którego język JavaScript stał się jednym z najważniejszych języków webowych. Czas więc wreszcie na jego nową odsłonę, a właściwie ewolucję - ostateczna definicja ECMAScript 6 ma się pojawić w czerwcu 2015 dlatego też pora na zapoznanie się z najważniejszymi nowościami jakie wprowadza. Oczywiście to nie oznacza, że od razu będziemy mogli używać wszystkich nowości - wiele zależy od producentów przeglądarek, którzy będą musieli dostosować swoje produkty do nowego standardu. Prace nad tym już jednak trwają, zresztą więcej na ten temat na końcu artykułu.

OK, zobaczmy co nowego w ECMAScript 6!

Klasy i dziedziczenie

Na początek temat, o którym głośno tutaj, na moim blogu ;). W ECMAScript 6, wprowadzona zostanie nowa składnia, która ma ułatwić to zagadnienie. Zobaczmy przykład:

class Car {
    constructor(name) { // konstruktor
        this.name = name
    }
}

class FamilyCar extends Car { // dziedziczenie
    constructor(name, maxSpeed) {
        super(name); // wywołanie bazowego konstruktora
        this.maxSpeed = maxSpeed;
    }
}

var familyCar = new FamilyCar('Volvo', 120);

console.log(familyCar.name);
console.log(familyCar.maxSpeed);

Myślę, że powyższy przykład nie wymaga dogłębnego wyjaśniania. Jak widać dodano kilka słów kluczowych takich jak class, extends czy super, które mają upodobnić tworzenie klas i dziedziczenie do innych znanych języków programowania. Tyle tylko, że jest tu jeden haczyk… to wszystko to tzw. “syntactic sugar” - pod spodem nadal odbywać się ma dziedziczenie prototypowe (zresztą wszystko o czym dziś piszę, to ewolucja a nie rewolucja - kod napisany po staremu nadal będzie działać). Z tego względu, moim zdaniem powyższe spowoduje jeszcze większy “misz masz” niż to jest obecnie. Wprowadzenie klas jest sztuczne w języku bez kontroli typów i wydaje mi się, że wywoła to więcej szkody niż pożytku

Przy pierwszym przykładzie przedstawiam dodatkowo fajne narzędzie, podobne do jsfiddle.net. Chodzi konkretnie o es6fiddle.net… dzięki któremu możecie przetestować powyższy przykład klikając tutaj.

Funkcje z użyciem strzałek

Kolejną nowością jest nowa składnia upraszczająca tworzenie “inline” funkcji wywołania zwrotnego. Moim zdaniem jest to jedno z ważniejszych usprawnień, ponieważ występuje ono w wielu językach programowania i programiści generalnie są do tego przyzwyczajeni. Jeśli spojrzycie na przykłady poniżej, na pewno od razu będziecie wiedzieli o co chodzi. Na początek kod po staremu:

var result,
    numbers = [1,2,3,4,5];

result = numbers.map(function(x) {
    return x + x;
});

setTimeout(function() {
  result = 'wynik: ' + result;
  console.log(result);
}, 2000);

Mamy tutaj dwie funkcje wywołania zwrotnego - jedna przekazywana do funkcji map, druga do setTimeout. Myślę, że wiadomo o co chodzi… To teraz zobaczcie jak to możemy napisać po nowemu:

var result,
    numbers = [1,2,3,4,5];

result = numbers.map(x => x + x);

setTimeout(() => {
    result = 'wynik: ' + result;
    console.log(result);
}, 2000);

Najważniejsze są zaznaczone linie. W pierwszej z nich widzimy, że zamiast funkcji występuje wyrażenie z =>. Zamiast pisać całą funkcję wystarczy jedna linijka, która robi to samo. W przypadku funkcji, która zawiera tylko wyrażenie z return, możemy je nawet pominąć.

W drugiej zaznaczonej linii, również mamy funkcję zastąpioną =>. Tym razem nie ma żadnej zmiennej przekazywanej do tej funkcji, dlatego zamiast niej mamy puste nawiasy. Podobnie wyglądałaby sytuacja z więcej niż jedną zmienną. Wówczas użylibyśmy nawiasów i wymienili wszystkie zmienne po przecinku, na przykład: (x, y).

Nie wiem jak Wy, ale mi się to usprawnienie bardzo podoba. Pod tym linkiem przykład do przetestowania.

Zakres zmiennych

A teraz coś, na co na pewno wielu programistów czekało… Jak na pewno wszyscy doskonale wiecie, w JavaScript widoczność zmiennych zawsze ograniczona jest od zakresu funkcji, w której się dana zmienna znajduje. W ECMAScript 6 natomiast zostanie wprowadzone nowe słowo kluczowe, które pozwoli definiować zmienne ograniczone do bloku (nawiasy klamrowe). Spójrzcie na przykład poniżej:

var num = 0;

if (num === 0) {
  let inScopeTemp = 2;
  var globalTemp = 3;

  for (let i = 0; i < 0; i++){
  	num += (inScopeTemp + globalTemp) * 1;
  }

  console.log(typeof i); // undefined - poza zakresem
}

console.log(typeof inScopeTemp); // undefined - poza zakresem
console.log(typeof num); // number - zakres globalny
console.log(typeof globalTemp); // number - zakres globalny

Na pewno od razu zauważyliście wspomniane słowo kluczowe - dla mniej spostrzegawczych chodzi o let w zaznaczonych linijkach ;) Jak pokazują testy zmiennych na końcu przykładu, rzeczywiście jego użycie zamiast var powoduje ograniczenie zmiennej do bloku. Uprzedzając pytania - jeśli użyjemy let poza jakąkolwiek funkcją, to zachowanie będzie tożsame z użyciem var.

Ktoś mógłby zapytać po co nowe słowo kluczowe - jak już wspomniałem mamy do czynienia z ewolucją więc stary kod musi działać po staremu. Pod tym linkiem powyższy przykład do przetestowania.

Obiekty Promise

Ameryki ta zmiana nie odkrywa. Mówiąc krótko, jest to przeniesienie do standardu konceptu znanego już chociażby z jQuery - pisałem o nim w jednym z poprzednich postów więc jeśli nie wiesz o co chodzi to zachęcam do zapoznania się z tym wpisem zanim przeanalizujesz kolejny przykład:

function doSomething() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('robię coś...');
      resolve();
    }, 1500);
  });
}

doSomething()
  .then(() => console.log('a teraz robię coś jeszcze'));

Jak widać, mamy dostępny obiekt Promise, który podczas tworzenia przyjmuje funkcję wywołania zwrotnego ze zmiennymi resolve i reject. Na końcu przykładu mamy wywołanie funkcji zwracającej obiekt Promise - razem z nią, łańcuchowo wywołać możemy funkcję then, która wywoła swojego “callbacka” dopiero kiedy wywołane zostanie resolve (lub reject) obiektu obietnicy. Znając koncepcję obiektów Promise na pewno wiecie o co tutaj chodzi ;)

Przykład do poklikania tutaj.

Moduły

Ostanie z omawianych dziś przeze mnie nowości. Ogólnie rzecz ujmując jest to wprowadzenie do standardu koncepcji modułów, które zostały już zaimplementowane w takich bibliotekach jak na przykład RequireJS.

Poniżej bardzo prosty przykład ogólnej koncepcji:

// ---- lib/math.js ----
let notExported = 123;

export function sum(x, y) {
    return x + y;
}

export var pi = 3.141593;

// ---- app.js ----
// nazwa modułu na podstawie strutkury folderów
import { sum as sumarize, pi } from 'lib/math'

console.log('2n = ' + sumarize(pi, pi));

Generalnie w jednym pliku możemy definiować co ma zostać wyeksportowane za pomocą słowa kluczowego export, a w innym możemy to zaimportować za pomocą słowa kluczowego import. Jak widać można przezywać importowane funkcje i właściwości aby uniknąć konfliktów nazw itp. itd.

Temat modułów jest dość obszerny dlatego postanowiłem go tylko zasygnalizować, a jeśli Was on bardzo zaciekawił to polecam Wam przeczytać wpis na blogu Axela Rauschmayera - znajdziecie tam dużo więcej na temat modułów w ECMAScript 6.

ECMAScript 6 - co nowego: podsumowanie

To co dziś przedstawiłem to tylko próbka tego co znajdzie się w ostatecznej wersji ECMAScript 6. Pod tym linkiem znajdziecie przykłady praktycznie wszystkich nowości. Co prawda producenci przeglądarek jak na razie nie wprowadzili jeszcze wszystkich tych zmian do swoich implementacji JavaScriptu ale myślę, że nastąpi to dość szybko - tutaj znajdziecie tabelę przedstawiającą postęp prac i to nie tylko u najważniejszych graczy rynku przeglądarek ale i w nodejs oraz wielu innych. Warto więc już teraz zapoznawać się z tymi nowościami bo zapewne całkiem niedługo będziemy mogli ich używać!

P.S. Jeśli ktoś jest zainteresowany, to moja prezentacja, na podstawie której powstał ten wpis jest do zobaczenia tutaj.