Mastering Javascript - esencja EMCAScript 6 (cz. 1)
Ostatnio dorwałam parę artykułów o ES6 i ES7, więc postanowiłam przelać swoją wiedzę na papier, rozwinąć ją i przy okazji napisać dla Was ciekawe podsumowania.
Artykuł o ES6 jest w dwóch częściach i zawiera - moim zdaniem - najciekawsze zmiany. W celu uzupełnienia informacji polecam bibliografię, znajduje się tam książka, która dokładnie opisuje wszystkie smaczki :) Warto dodać, że w tym artykule pobawiłam się konwencją, większość zmiennych oraz nazw klas jest napisana po polsku, bez znaków diakrytycznych tylko i wyłącznie dlatego, że mnie to bawi :) Zapraszam do czytania!
Const (stałe)
Ostatni standard Google dość jasno wyraził się w kwestii stałych: każda zmienna do której wartość przypisujemy tylko raz powinna być zadeklarowana jako stała i zostać zapisana camelCasem. Wywołało to sporo dyskusji, z jednej strony w Javascript jest to powszechne, z drugiej zaś stałe od dawna kojarzą się z prostymi wartościami zapisanymi wielkimi literami. Konwencje i standaryzacja to kłopotliwe tematy, w tym artykule będzie panował model Googe'owski :)
Zmienne deklarujemy w poniższy sposób. Warto pamiętać - choć jest to absurdalnie logicznie - że nie przypisanie wartości spowoduje błąd.
const PI = 3.14
const PUSTY; // -> błąd
const kwiat = {
kolor: 'czerwony',
wielkość: 'ogromny'
}
kwiat = {} //wywoła błąd
kwiat.kolor = 'niebieski' //jest poprawny
I jeszcze jedno: nieprawidłowe jest jedynie ponowne przypisanie wartości do zmiennej. Jeśli jest referencją to bez problemu można zmienić wartość do której prowadzi.
Zmiany w scopingu
Javascript stał się bardziej... blokowy. W ramach pozbycia się długu technicznego oraz optymalizacji wydajnościowej wprowadzono zmienne lokalne, które można usuwać z pamięci po wykonaniu bloku. Deklarowanie zmiennych, które nie podlegają windowaniu (ang. hoisting), dostępnych w zakresie danego bloku wykonujemy poleceniem let
.
let ulubionaLiczba = 3
console.log(ulubionaLiczba) //3
{
let ulubionaLiczba = 4
console.log(ulubionaLiczba) //4
}
console.log(ulubionaLiczba) //3
Bloki tworzą wszystkie klamry - łącznie z tymi w pętlach, warunkach oraz jak w ES5 funkcjach.
Na co jeszcze trzeba uważać? W przeciwieństwie do var, ponowna deklaracja zmiennej z użyciem polecenia let prowadzi do błędu.
//...
if (x) {
var z = 32
}
var z = 13
Błędnie mówi się o tym, że let
to nowe var
. Jednak gdy w powyższym przykładzie zmienimy var
na let
i x spełni warunek, pojawi się błąd.
Arrow Functions (funkcje strzałkowe)
EMCAScript rozwinął swój zasób jeśli chodzi o programowanie funkcyjne. Funkcje strzałkowe nie tylko upraszczają zapis, ale także pozwalają zachować kontekst nadrzędnej funkcji.
const milaFunkcja = (imie) => {
return `Czesc ${imie}`
}
//...
element.addEventListener('click', (e) => {
this.element = e.target.value
this.wynik = () => {
return this.element + 3
}
);
Extended Parameter Handling (rozszerzone parametry)
Na sam początek najlepsze: domyślne wartości parametrów! Patrzcie jak upraszczają kod:
const hi = (imie = 'Jack') => {
return `Hi! ${imie}`
}
//A teraz wersja z ES5 i wcześniejszych
var hi = function(imie) {
if (typeof imie === 'undefined' || !imie) {
imie = 'Jack'
}
}
Kolejna zmiana to parametr o zmiennej liczbie argumentów. Nie wiem czy tłumaczenie funkcje wariadyczne (ang. variadic function[3]) jest poprawne.
Najpopularniejszy i najprostszy problem tego typu pewnie znacie ze studiów, książek lub kursów: napisz funkcję, która sumuje wszystkie podane argumenty. Najpopularniejszym rozwiązaniem jest określony typ umieszczający argumenty w tablicy, przewidziany przez twórców języka. W JavaScript jest to operator spread ...
const zsumuj = (...tablicaArgumentów) => {
let suma = 0;
tablicaArgumentów.forEach((number) => {
suma += number
});
return suma;
}
Pomijając, że powinniśmy sprawdzić czy argument na pewno jest liczbą, tak wygląda rozwiązanie tego problemu w ES6. Ale rozwińmy to, żeby pokazać jak zachowuje się operator spread z innymi argumentami.
const zsumuj = (max, ...tablicaArgumentów, min) => {
let suma = 0;
tablicaArgumentów.forEach((number) => {
if (suma < max && suma > min) {
suma += number
}
});
return suma;
}
Warto wiedzieć, że ...
jest również operatorem, którego możemy używać na przykład do łączenia tablic:
let trzycztery = [3, 4];
let liczby = [1, 2, ...trzycztery, 5, 6];
//wynikiem powyższego jest tablica z liczbkami od 1 do 6
Ale to nie koniec! Chcecie pushować tablicę na koniec innej?
let tab1 = [1, 2]
let tab2 = [3, 4]
tab1.push(...tab2)
//wynikie powyższego jest tablica z cyframi od 1 do 4
//jest to równe Array.prototype.push.apply(tab1, tab2);
Można go używać na wszystkich typach, które są iterowalne, czyli także na stringach. W tym przypadku zadziała jak funkcja split() , gdy jako argument przyjmuje pusty ciąg znaków.
let ciagZnakow = 'samochód';
let tablica = [...ciagZnakow]
// tablica jest równa ['s', 'a', 'm', 'o', 'c', 'h', 'ó', 'd']
String Interpolation (napisy szablonowe)
Deklaracje stringów w których istnieją symbole odnoszące się do zmiennych. Napisany szablonowe zastępują proste (z użyciem operatora dodawania) konkatenacje stringów w których występują zmienne.
const name = 'Kamila';
const welcomeText = `Hi ${name}! How are you?`;
//wynikiem powyższej konkatenacji jest 'Hi Kamila! How are you?'
Wygodne, prawda?
Enhanced Object Properties (rozszerzone właściwości obiektów)
Skrócenie inicjalizacji obiektów wydawało się oczywistością, nareszcie możemy zadeklarować taki oto obiekt:
const wymiarX = 10;
const wymiarY = 20;
const wymiarZ = 30;
const pudelko = {
wymiarX, wymiarY, wymiarZ
}
Dotyczy to również funkcji:
const okrag = {
srednica: 1,
zmierz() {
//...
},
zmniejsz (z = 1) {
//...
}
}
EMCAScript wszedł również na kolejny poziom abstrakcji pozwalając aby nazwa właściwości (klucz) była zmienną. Odbywa się to poprzez umieszczenie nazwy zmiennej w kwadratowych nawiasach:
const identyfikator = "PESEL";
let obj = {
[identyfikator]: '00AABB'
}
obj.PESEL // -> 00AABB
Imports and exports (import i eksporty)
Wcześniej import i eksport modułów odbywał się poprzez zmienne globalne, a teraz wczytuje się do scope'u pliku. Jest to gigantyczna zmiana na poziomie modularności Javascriptu.
//plik lib.js
export default {
func: () => {
//...
}
}
const wykonaj = () => {
//...
}
const zapisz = () => {
//...
}
export { wykonaj, zapisz }
// inny plik:
import library from "lib"
import { zapisz, wykonaj } from "lib"
//wywołania:
library.func();
zapisz();
wykonaj();
Wywołanie library.func()
jest inne ze względu na eksport domyślny całego obiektu, ale nic nie stoi na przeszkodzie, żeby wyeksportować samą funkcję.
Classes (klasy)
Czasem wydaje mi się, że EMCAScript stoi w rozkroku, ale nie mogę powiedzieć, że mi to przeszkadza. Ba! Jestem zachwycona ogromem możliwości. Pomimo rosnącej popularności programowania funkcyjnego, JavaScript z radością powitał klasy.
class Kot {
constructor (imie) {
this.imie = imie
}
miau (poziomGlosnosci = 100) {
this.poziomGlosnosci = poziomGlosnosci
}
}
Nie są to jednak wydmuszki, elementy programowania obiektowego takie jak dziedziczenie oraz funkcje/właściwości statyczne są bardzo intuicyjnie obsłużone.
class Zwierzak {
constructor (imie, typ = 'Animalia') {
this.imie = imie
this.typ = typ
}
dajGłos (poziomGlosnosci = 100) {
this.poziomGlosnosci = poziomGlosnosci
}
//...
}
class Kot extends Zwierzak {
constructor (imie) {
super(imie, typ = 'Felis')
}
static typowyKot = new Kot('Łatek')
}
Nie można zapomnieć także o getterach i setterach w klasach :)
class Zwierzak {
constructor (imie, typ) {
this.imie = imie
this.typ = 'Animalia'
}
dajGłos (poziomGlosnosci = 100) {
this.poziomGlosnosci = poziomGlosnosci
}
//...
}
class Kot extends Zwierzak {
constructor (imie) {
super(imie, typ = 'Felis')
}
get imie { return this.imie }
set imie (noweImie) { this.imie = noweImie }
}
Podsumowanie
Ze względu na obszerność tematu, podzieliłam wpis na dwie części. Mam nadzieję, że podoba się Wam to wejście w teorię Javascript, z pewnością będę kontynuowała serię Mastering JavaScript, chociaż rozwiązywanie problemów cieszy mnie o wiele bardziej :)
Bardzo dziękuję za wsparcie wszystkim czytelnikom, którzy nieśmiało wysyłali uwagi prywatnie. Jest ich tak dużo, że samo zabranie ich znacznie opóźniło publikację tego wpisu :)
Zachęcam do komentowania pod postem i dyskusji, bardzo chętnie usłyszę wszystkie uwagi :) A poniżej materiały do czytania:
- Kyle Simpson, EMCAScript 6 i Dalej, Helion 2016 - gruba książka, którą pożeram właśnie
- http://es6-features.org/
- https://en.wikipedia.org/wiki/Variadic_function
- https://exploringjs.com/es6/index.html
- https://developer.mozilla.org/pl/docs/Web/JavaScript/Referencje/Operatory/Spread_operator