javascript

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:

Kamila

Dziękuję za poświęcony czas, będzie mi bardzo miło jak zostawisz komentarz :)