ES6 – default + rest + spread

javascript

Biorąc pod uwagę całe lata narzekań, ECMAScript 2015 przynosi nam wiele nowości, czego rezultatem jest spora ilość usprawnień. Sprawiają one, że pisanie kodu w JavaScript jest bardziej intuicyjne oraz po prostu szybsze. Spójrzmy zatem na nowy sposób przekazywania parametrów do funkcji.


Zobacz całą serię: Let-s talk about ECMAScript 2015


default

Jest to małe, ale bardzo przydatne usprawnienie w przekazywaniu parametrów do funkcji. Wiemy przecież, że funkcje w języku JavaScript pozwalają na przekazanie do nich dowolnej ilości parametrów. Z pewnością nie raz spotkaliście się z podobnym kodem:

function inc(number, increment) {
  // set default to 1 if increment not passed
  // (or passed as undefined)
  increment = increment || 1;
  return number + increment;
}
console.log(inc(2, 2)); // 4
console.log(inc(2));    // 3

Operator logiczny OR (||) przypisuje w tym wypadku wartość 1 do zmiennej increment, ponieważ lewy człon wyrażenia to undefined, czyli false.

ES6 pozwoli pozbyć się tego typu wyrażeń, wykorzystując w tym celu parametry z domyślnymi wartościami. Funkcja inc z wykorzystaniem składni ES6 prezentuje się zatem następująco:

function inc(number, increment = 1) {
  return number + increment;
}
console.log(inc(2, 2)); // 4
console.log(inc(2));    // 3

Tym samym parametry z wartościami domyślnymi są opcjonalne.

Możemy również definiować domyślne wartości parametrów, które występują w środku sygnatury funkcji:

function sum(a, b = 2, c) {
  return a + b + c;
}
console.log(sum(1, 5, 10));         // 16 -> b === 5
console.log(sum(1, undefined, 10)); // 13 -> b as default

Wykorzystanie wartości domyślnej następuje w tym wypadku w momencie, gdy jako wartość parametru podamy undefined.

Wartości domyślne nie muszą być typami prymitywnymi. Jako domyślną wartość możemy przypisać choćby rezultat wykonania funkcji:

function getDefaultIncrement() {
  return 1;
}
function inc(number, increment = getDefaultIncrement()) {
  return number + increment;
}
console.log(inc(2, 2)); // 4
console.log(inc(2));    // 3

rest

Spróbujmy napisać wcześniejszą funkcję sum tak, by brała pod uwagę wszystkie parametry jakie do niej przekażemy. By zachować czystość kodu, pominiemy tutaj walidację. Jeżeli próbowalibyśmy użyć do tego zadania składni dobrze znanej z ES5, to z pewnością otrzymalibyśmy coś takiego:

function sum() {
   var numbers = Array.prototype.slice.call(arguments),
       result = 0;
   numbers.forEach(function (number) {
       result += number;
   });
   return result;
}
console.log(sum(1));             // 1
console.log(sum(1, 2, 3, 4, 5)); // 15

Przy takim podejściu nie wiemy od razu co robi dana funkcja. Nie wystarczy, że spojrzymy na jej sygnaturę. Musimy jeszcze przeskanować jej ciało, znaleźć obiekt arguments i wywnioskować, że chodzi tutaj o zsumowanie wszystkich parametrów przekazanych do funkcji.

Składnia ES6 znacznie lepiej radzi sobie z podobnym problemem . Wprowadza ona pojęcie rest parameters, gdzie parametr poprzedzony (trzema kropkami) staje się tablicą, do której “wpadają” wszystkie pozostałe parametry przekazane do funkcji.

Obiekt arguments zawierać będzie wszystkie parametry funkcji - te nazwane i nie.

Możemy użyć nowej składni ECMAScript 2015 i przepisać funkcję sum:

function sum(…numbers) {
  var result = 0;
  numbers.forEach(function (number) {
    result += number;
  });
  return result;
}
console.log(sum(1)); // 1
console.log(sum(1, 2, 3, 4, 5)); // 15

Jest jednak jedno ALE: nie możemy posiadać więcej niż jednego parametru typu …rest. W przeciwnym razie otrzymamy błąd jak niżej:

function sum(…numbers, last) { // causes a syntax error
  var result = 0;
  numbers.forEach(function (number) {
    result += number;
  });
  return result;
}

spread

Kolejne wyrażenie, to spread, które przypomina trochę konstrukcję rest ze względu na swoją notację z trzema kropkami. Dzieli tablicę na poszczególne wartości, które przekazywane są jako osobne parametry do ciała funkcji.

Po raz kolejny zdefiniujmy funkcję sum, do której przekażemy wyrażenie typu spread:

function sum(a, b, c) {
  return a + b + c;
}
var args = [1, 2, 3];
console.log(sum(…args)); // 6

Odpowiednik takiego kodu w ES5, to:

function sum(a, b, c) {
  return a + b + c;
}
var args = [1, 2, 3];
console.log(sum.apply(undefined, args)); // 6

Zamiast więc używać funkcji apply możemy od teraz posługiwać się “rozbiciem” tablicy na osobne parametry. Rozwiązanie prostsze i według mnie również bardziej czytelne.

Ciekawostką jest fakt, że wyrażenie spread możemy łączyć ze standardowym sposobem przekazywania parametru do funkcji:

function sum(a, b, c) {
  return a + b + c;
}
var args = [1, 2];
console.log(sum(…args, 3)); // 6

Rezultat jest dokładnie taki sam jak w poprzednim przykładzie. Tym razem tablica z dwiema wartościami zostaje rozbita na dwa parametry odpowiadające ab, zaś trzecim parametrem jest liczba 3, która odpowiada zmiennej c.

ES6 – arrow functions

javascript

Kolejnym elementem standardu ECMAScript 2015 będą bez wątpienia arrow functions. Znane również jako fat functions, pozwolą tworzyć kod bez – zbędnego w wielu przypadkach – słowa kluczowego function oraz ze zbindowanym this do ciała funkcji.


Zobacz całą serię: Let-s talk about ECMAScript 2015


Syntactic sugar

(w oryginalnym artykule użyłem właśnie tego nagłówka, jako, że polski odpowiednik brzmi idiotycznie)

Spójrzmy na sygnaturę nowej składni anonimowej funkcji:

([param] [, param]) => {
 statements
}
           param => expression
(param1, param2) => { block }

, którą możemy użyć w następujący sposób:

    () => { … }            // no argument
     x => { … }            // one argument
(x, y) => { … }            // several arguments
     x => { return x * x } // block
    x => x * x             // expression, same as above
Pamiętaj: każda tak tworzona funkcja jest anonimowa!

Wyrażenia lambda w JavaScript! Wspaniale :)

Zamiast pisać:

[3, 4, 5].map(function (n) {
 return n * n;
});

możemy po prostu zrobić coś takiego:

[3, 4, 5].map(n => n * n);

Już jest dobrze, a jest coś jeszcze..

Fixed “this” = lexical “this”

Funkcja tworzona przy pomocy konstrukcji z => binduje this wewnątrz ciała funkcji w miejscu jej deklaracji, a nie użycia. Tym samym nie musimy już używać funkcji bind, call czy apply. Nie musimy również tworzyć konstrukcji typu:

var self = this;

Dla mnie to naprawdę spore ułatwienie, a przy okazji sposób na lepszą optymalizację kodu.

// ES5
function FancyObject() {
 var self = this;
 
 self.name = 'FancyObject';
 setTimeout(function () {
  self.name = 'Hello World!';
 }, 1000);
}

// ES6
function FancyObject() {
 this.name = 'FancyObject';
 setTimeout(() => {
  this.name = 'Hello World!'; // properly refers to FancyObject
 }, 1000);
}

Konstrukcja funkcji za pomocą =>, to:

  • typeof zwracające function
  • instanceof zwracające Function

, ale niestety także kilka niedogodności:

  • Tak stworzona funkcja nie może być użyta w formie konstruktora i każde jej wywołanie ze słowem new rzuci wyjątek.
  • Fixed this oznacza, że wartość zbindowana wewnątrz funkcji nie może zostać zmieniona przez cały cykl jej życia.
  • Funkcja nie może być funkcją nazwaną.
  • Nie mamy samej deklaracji funkcji, stąd nie istnieje tutaj hoisting pozwalający użyć danej funkcji przed jej inicjalizacją.

ES6  – let + const

javascript

Pierwszy temat związany z nadchodzącym ECMAScript 2015, czyli letconst. Oba pojęcia bardzo mocno związane są ze scope deklarowanych zmiennych, co postaram się pokazać w porównaniu ze sposobem deklaracji zmiennych za pomocą słowa kluczowego var.


Zobacz całą serię: Let-s talk about ECMAScript 2015


Spójrzmy wpierw jednak na przykład pokazujący użycie var i deklaracji zmiennych znanych z ES5 i wcześniej.

var a = 1;
 
if (1 === a) {
 var b = 2; 
}
 
for (var c = 0; c < 3; c++) {
 // …
}
 
function letsDeclareAnotherOne() {
 var d = 4;
}
 
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(d); // ReferenceError: d is not defined
 
// window
console.log(window.a); // 1
console.log(window.b); // 2
console.log(window.c); // 3
console.log(window.d); // undefined
  1. Zmienna a deklarowana jest jako globalna.
  2. Zmienna b deklarowana jest wprawdzie wewnątrz bloku if, ale jest ona również widoczna poza nim.
  3. Podobnie jak deklaracja b, zmienna c deklarowana jest w bloku for, który nie ogranicza jej swoim zasięgiem.
  4. Dopiero zmienna d posiada swój własny scope, ponieważ ograniczona jest ciałem funkcji.
Variables in JavaScript are hoisted to the top!

Hoisting, to w JavaScript domyślne zachowanie w momencie deklaracji zmiennych = wynoszenie dekleracji na samą górę skryptu lub ciała funkcji.

Tym samym zmienna w języku JavaScript może być użyta zanim zostanie zainicjalizowana. Różnicę w dekleracji zmiennych, a ich inicjalizacji przedstawia poniższy przykład:

var n = 1;
 
(function () {
 console.log(n);
 var n = 2;
 console.log(n);
})();
 
console.log(n);

let

Spójrzmy teraz na nowy sposób deklaracji zmiennych, który bezpośrednio wpływa na ich zakres działania.

let a = 1;
 
if (1 === a) {
 let b = 2; 
}
 
for (let c = 0; c < 3; c++) {
 // …
}
 
function letsDeclareAnotherOne() {
 let d = 4;
}
 
console.log(a); // 1
console.log(b); // ReferenceError: b is not defined
console.log(c); // ReferenceError: c is not defined
console.log(d); // ReferenceError: d is not defined
 
// window
console.log(window.a); // 1
console.log(window.b); // undefined
console.log(window.c); // undefined
console.log(window.d); // undefined

Jest to ten sam przykład kodu, który podawałem wyżej dla ES5. Tym razem jednak widzimy różnice w wartościach, które otrzymamy w konsoli przeglądarki. Tylko zmienna a zadeklarowana została jako globalne, a wszystkie pozostałe mają zasięg blokowy.

const

W przypadku użycia const otrzymamy dokładnie takie same wyniki jak dla deklaracji zmiennych z użyciem słowa kluczowego let. const tym samym również pozwala deklarować zmienne o zasięgu blokowym. Różni się jednak od let (i var również) tym, że raz zainicjalizowana wartość zmiennej nie może zostać nadpisana. Można było się tego domyślać po nazwie const (stała).

{
 const PI = 3.141593;
 PI = 3.14; // throws “PI” is read-only
}
 
console.log(PI); // throws ReferenceError: PI is not defined

Każda próba nadpisania zmiennej utworzonej za pomocą słówka const skończy się rzuceniem wyjątku przez interpeter JavaScript.

Powyższy przykład w ES5 mógłby wyglądać następująco:

var PI = (function () {
 var PI = 3.141593;
 return function () { return PI; };
})();

ES6 – Let’s talk about ECMAScript 2015

javascript

Czas na powrót wraz z serią Let’s talk about ECMAScript 2015, którą miałem okazję tworzyć na @medium.com. Wracać tutaj nie zamierzałem, ale ostatecznie zdecydowałem, że przetłumaczę wszystkie artykuły na język polski, a tym samym wniosę trochę świeżości na bloga.


Zobacz całą serię: Let-s talk about ECMAScript 2015


Oficjalna specyfikacja ECMAScript 2015 niedawno ujrzała światło dzienne i tym samym przygoda z ES6 zaczyna stanowić codzienność w JavaScriptowym świecie.

ES6 to znacząca zmiana dla języka JavaScript. Poprzednia wersja istnieje na rynku od 2009 roku. W tym czasie pojawiły się (i / lub rozwijały) frameworki takie jak AngularJS, Backbone, Ember, Meteor, Aurelia, ReactJS, Ionic i wiele innych. Ich kolejne wersje będą już wykorzystywać dobrodziejstwa, które oferuje standard ECMAScript 2015.

ECMAScript 2015 to szereg nowości:

  • arrows
  • classes
  • enhanced object literals
  • template strings
  • destructuring
  • default + rest + spread
  • let + const
  • iterators + for..of
  • generators
  • unicode
  • modules
  • module loaders
  • map + set + weakmap + weakset
  • proxies
  • symbols
  • subclassable built-ins
  • promises
  • math + number + string + object APIs
  • binary and octal literals
  • reflect api
  • tail calls

Celowo zachowuję tutaj angielskie nazewnictwo, ponieważ tytuły kolejnych wpisów będą je odzwierciedlać. Znaczącą większość zakresu udało mi się opisać na bazie draftu specyfikacji, który istniał przed pojawieniem się oficjalnej wersji.

Producenci przeglądarek są na dobrej drodze, by z czasem w pełni wspierać nowy standard. Natomiast dzisiaj, dzięki takim narzędziom jak Babel, czy Traceur, możemy w dużej mierze korzystać z możliwości ES6 już teraz.

Zobacz: aktualne wsparcie przeglądarek dla ES6.

Wielokrotnie podkreślałem w swoich artykułach stwierdzenie: Future is bright. Jesteśmy na dobrej drodze by wypchnąć JavaScript na kolejny poziom.


ECMAScript 2015 - Let's talk about ES6

PS. Na bazie serii powstała również książka ECMAScript 2015 – Let’s talk about ES6, którą możecie poczytać ZA DARMO ONLINE.

Reorganizacja i przenosiny na @medium.com

medium.com

Jakiś czas temu postanowiłem sobie, że zacznę pisać po angielsku, bo:

  • Przecież nikt nie czyta treści związanych z IT po polsku (A czyta? Mi się zdarza, ale w 99% zaglądam raczej do anglojęzycznych źródeł);
  • Chcę dotrzeć do większej społeczności (Głównie chodzi o komentarze i wymianę opinii);
  • Chcę odbudować, a potem dalej rozwijać swoje umiejętności językowe (Nie mam na to szans na co dzień, więc warto postawić przed sobą wyzwanie).

Postanowienie zacząłem przekuwać w działanie. Zarejestrowałem domenę, znów postawiłem bloga od zera, wykorzystując w tym celu dobrze sobie znaną platformę Wordpress. Zrobiłem wszystko, by tylko upodobnić całość do mojego ulubionego serwisu @medium.com. I co? I nie napisałem tam ani jednego wpisu. Mógłbym się tłumaczyć dlaczego, ale przecież nie o to chodzi. Przemyślałem tym samym sprawę i skupię się na tym, na czym zależy mi najbardziej.. na pisaniu. Wtedy gdy będę miał na to czas, ochotę i pomysł. Ot i tyle.

Jeżeli zamierzacie śledzić mnie dalej, to zapraszam na profil: @mrzepinski Już niedługo pojawi się tam seria wpisów o ECMAScript 2015 (ES6).

Niniejszą domenę postaram się utrzymać, ale po nowe wpisy zapraszam jednak do nowego miejsca. Mam też nadzieję, że Wam się spodoba.