es6-cz.1

Co wprowadziło ES6 do JavaScript cz. 1

Podziel się ze znajomymi

Od wprowadzenia w życie standardu ES6 minęło już niemal 5 lat. Mimo to wiele osób nadal nie orientuje się co takiego zostało zmienione. Pomysł na mini cykl o ES6 pojawił się, kiedy sam zapoznawałem się ze zmianami jakie zostały wtedy wprowadzone. Jak wspominałem w Podsumowanie roku 2019, na blogu będą pojawiały się wpisy o JavaScripcie, a to jest pierwszy z całego cyklu.

Czym jest ECMAScript?

ECMAScript to specyfikacja skryptowego języka programowania, stworzona przez ECMA. JavaScript jest jedną z najbardziej znanych implementacji. Inne implementacje to JScript i ActionScript. Odpowiedzialny jest za definiowanie semantyki języka oraz niektórych typów danych i obiektów, np. String, Boolean, Number, Object, Math, Array. Specyfikacja ECMAScript nie zawiera modelu dokumentu, funkcji wejścia-wyjścia oraz obsługi GUI. 

ES6 zostało opublikowane w 2015 roku, a wraz z nim zostało zmienione nazewnictwo. Od tego momentu oficjalna nazwa to ES2015. Kolejne wersje to ES2017, ES2018, etc.. Wiele wprowadzonych zmian ułatwia życie programistom oraz poprawia czytelność ich kodu.

Wiemy już czym jest ECMAScript, zatem możemy przejść do omawiania tego co wprowadził standard ES2015.

Funkcje strzałkowe

Arrow Functions nazywane również funkcjami strzałkowymi, to skrócony zapis funkcji anonimowych.  Pozwalają na poprawienie czytelności kodu co zaprezentuję na poniższych przykładach.

Uproszczenie kodu

Zawsze powinniśmy dbać o jakość kodu. Tak by inni programiści mogli w szybki i prosty sposób rozczytać to co napisaliśmy i dodać nowe funkcjonalności. Dlatego zaczniemy omawianie arrow functions od zaprezentowania nowych możliwości poprawiających czytelność kodu. Jednak wcześniej pokaże przykład kodu napisany w ES5.

Przykład ES5

var result = function(a, b) {
  return a + b;
}
console.log(result(5, 5)) // rezultat w konsoli -> 10

Jak możemy zobaczyć powyżej, tworzymy zmienną result do której przypisujemy funkcję anonimową, która przyjmuje dwa parametry: a i oraz zwraca ich sumę. W ostatniej linijce przedstawionego powyżej kodu znajduje się wywołanie funkcji i wyświetlenie rezultatu w konsoli.

Przykład ES6

var result = (a, b) => {
  return a + b;
}
console.log(result(5, 5)) // rezultat w konsoli -> 10

Teraz utworzyliśmy dokładnie taką samą funkcję anonimową, która zwraca dokładnie ten sam wynik. Różnica w obu kodach polega na sposobie zapisu. Funkcję strzałkową zapisujemy za pomocą => następnie otwieramy nowy blok i zwracamy sumę przekazanych parametrów. Początkowo może się ten zapis wydawać dziwny i niezrozumiały jednak z czasem przekonacie się, że jest to zdecydowanie wygodniejszy sposób niż ten z pierwszego fragmentu kodu. Jednak omawiana przez nas funkcja może zostać zapisana jeszcze prościej.

Przykład ES6

var result = (a, b) => a + b;
console.log(result(5, 5)) // wynik wyświetlony w konsoli -> 10

Jak możemy zauważyć powyżej, kod został uproszczony do 2 linijek. Spełnia dokładnie takie samo zadanie jakie realizował kod z pierwszego przykładu. Różnica względem poprzedniego kodu polega na tym, że w przypadku funkcji, których zadaniem jest przetworzenie danych i zwrócenie wyniku możemy zastosować powyższy zapis, który domyślnie zwróci wartość danego wyrażenia. W naszym przypadku wyrażenia a + b.

Aby zrozumieć funkcje strzałkowe, należy zapoznać się ze sposobami w jaki możemy je tworzyć.

() => {} – funkcja strzałkowa, która nie przyjmuję żadnych parametrów, a w jej ciele wykonywane są conajmniej dwie operacje.

() => Math.pow(MathPI) – funkcja strzałkowa, która nie przyjmuję żadnych parametrów, a w jej ciele zwracany jest wynik wykonywanej operacji.

() => ({ name: ‚Adam’, surname: ‚Kowalski’ }) – funkcja strzałkowa, która nie przyjmuje żadnych parametrów, a w jej ciele zwracany jest wynik w postaci obiektu. Nawiasy okrągłe dodajemy zawsze, gdy zwracany jest obiekt.

param => {} – funkcja strzałkowa, która przyjmuje jeden parametr, a w jej ciele wykonywane są conajmniej dwie operacje.

r => 2 * Math.PI * r – funkcja strzałkowa, która przyjmuje jeden parametr, a w jej ciele zwracany jest obwód koła na podstawie parametru będącego promieniem.

r => ({ radius: r, circuit: 2 * Math.PI * r }) – funkcja strzałkowa, która przyjmuje jeden parametr, a w jej ciele zwracany jest wynik w postaci obiektu.

(a, b) => {} – funkcja strzałkowa, która przyjmuje dwa parametry, a w jej ciele wykonywane są conajmniej dwie operacje.

(a, b) => a + b – funkcja strzałkowa, która przyjmuje dwa parametry, a w jej ciele zwracany jest wynik operacji.

W przypadku przekazywania większej liczby parametrów postępujemy tak samo jak w funkcji z dwoma parametrami.

Brak zmiany kontekstu

Przed wprowadzeniem standardu ES2015 częstą praktyką było przypisywanie this do zmiennej. Być może spotkałeś się z zapisem

var self = this;

I późniejszym odwoływaniem się do zmiennej self zamiast do obiektu this.

Na początek zobaczmy przykład wykorzystujący powyżej omówioną praktykę.

function Counter() {
  var self = this;
  self.count = 0;

  setInterval(function() {
    self.count += 1;
    console.log(self.count);
  }, 1000);
}

var counter = new Counter();

W pierwszej linijce funkcji tworzona jest zmienna self, a następnie zostaje zainicjalizowane pole count. Wywołanie metody setInterval() wymaga podania w pierwszym argumencie funkcji. Gdybyśmy chcieli w tej funkcji odwołać się za pomocą this, wtedy ciągle otrzymywalibyśmy w konsoli wartość 0, ponieważ słowo kluczowe function tworzy własny zakres zmiennych. Dlatego wykorzystujemy wcześniej utworzoną zmienną self.

Dzięki funkcji strzałkowej unikamy tego problemu, ponieważ arrow function nie tworzy nowego kontekstu. Poniżej możemy zobaczyć kod wykonujący dokładnie to samo co powyższy jednak został on zapisany w standardzie ES6.

function Counter() {
  this.count = 0;
  setInterval(() => {
    this.count += 1;
    console.log(this.count);
  }, 1000);
}
  
var counter = new Counter();

Teraz nie tworzymy już zmiennej self, a wywołując metodę setInterval() podajemy w pierwszym parametrze funkcję strzałkową. Dzięki temu możemy zmienić pole count korzystając z obiektu this.

Jak mogliśmy zobaczyć w tej części wpisu, funkcje strzałkowe pozwalają na tworzenie funkcji nie zmieniających zakresu this oraz poprawiają czytelność pisanego kodu.

Blokowy zakres zmiennych

ES2015 wprowadza możliwość deklarowania zmiennych blokowych poprzez wykorzystanie słowa kluczowego let. Wcześniej programiści korzystali z funkcyjnego zakresu zmiennych poprzez stosowanie słowa kluczowego var.

Zacznijmy od wyjaśnienia różnic pomiędzy blokowym i funkcyjnym zakresem zmiennych. Zakres funkcyjny rozpoczyna się od miejsca w którym tworzona jest nowa funkcja. Chcąc utworzyć zmienną lokalną programiści wykorzystywali tzw. domknięcie.

var counter = 0; //zmienna globalna
console.log(counter);

(function () {
  var counter = 5; //zmienna lokalna
  console.log(counter);
})();
console.log(counter);

Należy jednak pamiętać, że instrukcje warunkowe oraz pętle nie są funkcjami przez co programiści musili oplatać je w domknięcia, aby móc korzystać wewnątrz pętli ze zmiennych lokalnych.

var i = 'Bugajsky.pl';

(function () {
  for (var i = 0; i < 10; i++) { // zmienna lokalna
      console.log(i) // wyświetli w konsoli liczby od 0 do 9
  }
})();

console.log(i); // wyświetli Bugajsky.pl

Jak już wspomniałem, ES2015 pozwala na deklarowanie zmiennych z zakresem blokowym za pomocą słowa let. Poniżej znajduje się kod wykonujący tę samą operację co poprzedni jednak tym razem jest zapisany z wykorzystaniem let.

let i = 'Bugajsky.pl';

  for (let i = 0; i < 10; i++) { // zmienna lokalna
      console.log(i) // wyświetli w konsoli liczby od 0 do 9
  }

console.log(i); // wyświetli Bugajsky.pl

Zalecane jest wykorzystywanie let mimo, że nadal mamy możliwość korzystania z var. Nowe słowo kluczowe powinno być wykorzystywane nie tylko do deklarowania zmiennych lokalnych, ale także zmiennych globalnych. Będą dostępne w całym programie chyba, że zostaną nadpisane.

W nowym standardzie zostało również dodane słowo kluczowe const, które pozwala nam na deklarowanie stałych. 

Różnice pomiędzy let a const

  • deklarując stałą musimy przypisać jej wartość,
  • po zadeklarowaniu stałej nie ma możliwości zmiany jej wartości

Poprawna deklaracja stałych

const name = 'bugajskypl';

Błędne korzystanie ze stałych

const name;
const surname = 'Bugajski';
surname = 'Bugajski2';

Zalecane jest używanie let w sytuacjach kiedy następuje zmiana wartości, np. jako licznik w pętli. Warto też pamiętać, że const w przypadku obiektów oraz tablic przechowuje referencje do danych. Dzięki temu możemy modyfikować takie stałe, ponieważ modyfikacje nie zmieniają referencji.

const person = {
  name: 'Adam',
  surname: 'Kowalski'
}
person.city = 'Warszawa'
console.log(person)

const people = [{ name: 'Adam', surname: 'Kowalski' }]
people.push({ name: 'Kamil', surname: 'Nowak' })
console.log(people)

Jednak nadal nie możemy przypisać nowego obiektu.

const person = {
  name: 'Adam',
  surname: 'Kowalski'
}

person = {
  name: 'Kamil',
  surname: 'Nowak'
}

Klasy

Wraz z pojawieniem się standardu ES2015 zostały także dodane klasy. Od tej pory programiści mogli je tworzyć w podobny sposób jak odbywa się to w przypadku innych, obiektowych języków programowania. Do tej pory zmuszeni byli do wykorzystywania prototypów i funkcji-konstrukcyjnych. Poniżej przykład.

function Car(brand) {
  this.brand = brand;
}

Car.prototype.showCar = function() {
  console.log(this.brand);
}

var car = new Car('Seat')
console.log(car)

Wcześniejszy zapis klas nie był na tyle intuicyjny jak w innych językach programowania. Na szczęście zostało to zmienione i teraz tworzenie klas jest przyjemniejsze.

class Car {
  constructor(brand) {
    this.brand = brand;
  }

  showCar() {
    console.log(this.brand);
  }
}

var car = new Car('Seat')
console.log(car)

Jak możemy zobaczyć powyżej, klasę deklarujemy za pomocą słowa kluczowego class, a metodę tworzącą obiekt za pomocą constructor. Obiekt tworzymy w podobny sposób jak w ES5.

Dostaliśmy również możliwość dziedziczenia właściwości.

class SportCar extends Car {
  constructor(brand, diffuser) {
    super(brand);
    this.diffuser = diffuser;
  }
}

Chcąc dodać do klasy dziedziczenie musimy skorzystać ze słowa extends po czym podać klasę po której będziemy dziedziczyć właściwości. Jeżeli klasa bazowa Car wymaga podania parametrów w konstruktorze musimy to zrobić wywołując metodę super(params) w konstruktorze klasy pochodnej SportsCar.

ES2015 nie umożliwia programistom tworzenia pól i metod z podziałem na publiczne i prywatne.

Warto też zaznaczyć, że tworzenie klas w nowy sposób to tak zwany syntatic sugar. Oznacza to, że jedynie zmienia się sposób zapisu klas, a w rzeczywistości nadal wykorzystywane są prototypy.

Podsumowanie

To nie wszystkie zmiany jakie przyniósł standard ES2015. Jednak nie chciałem by wpis był za długi, dlatego podzieliłem go na części. Jak mogliśmy zobaczyć, omówione nowości znacznie poprawiają czytelność kodu, a także ułatwiają jego pisanie. Funkcje strzałkowe skracają zapis oraz pozwalają zapomnieć o przypisywaniu obiektu this do zmiennej. Blokowy zakres zmiennych umożliwia tworzenie wielu pętli wykorzystujących licznik o tej samej nazwie. Uproszczony zapis klas pozwala na łatwiejsze rozczytanie istniejącego kodu, ponieważ jest teraz bardziej intuicyjny.

Jeżeli podoba Ci się to co robię na blogu, wesprzyj mnie obserwując moje profile społecznościowe Fanpage, Twitter oraz Instagram. Dzięki temu nie ominie Cie żaden wpis.

Dodaj komentarz

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