To kolejny wpis o zmianach jakie zostały wprowadzone na przestrzeni ostatnich lat w JavaScript. W tym artykule przyjrzymy się bliżej zmianom wprowadzonym w 2017 roku. o ile chcecie zapoznać się ze zmianami wprowadzonymi przed 2017 rokiem, to zachęcam Was do przeczytania moich wpisów:
- Co wprowadziło ES6 do JavaScript cz. 1
- Co wprowadziło ES6 do JavaScript cz. 2
- Co wprowadziło ES6 do JavaScript cz. 3
- Co wprowadziło ES7 do JavaScript
Object.values/Object.entries
Podobnie jak Object.keys zwracają tablice, której kolejność jest zgodna z kolejnością Object.keys. Każda pozycja w tablicy zawiera odpowiednio klucz i wartość atrybutu iterowanego obiektu.
Przed ES8 programiści chcący iterować po utworzonym obiekcie, musieli używać Object.keys i iterować po zwróconej tablicy. Zmuszeni byli do korzystania z zapisu obj[key], aby uzyskać wartość danego elementu. Poniżej znajduję się przykład
const person = {firstName: 'Adam', lastName: 'Kowalski', age: 30} Object.keys(person).forEach(key=>{ console.log(`${key}: ${person[key]}`) })Mogli także wykorzystać do tego celu pętlę for of.
const person = {firstName: 'Adam', lastName: 'Kowalski', age: 30} for (let key of Object.keys(person)) { console.log(`${key}: ${person[key]}`) }Poza tymi dwoma metodami, programiści mogli jeszcze wykorzystać pętlę for in. Pętla ta iteruje po wszystkich adekwatnościach, np. prototype, a nie tylko tych, które utworzył programista. Może to powodować pewne błędy w działaniu.
Object.values zwraca tablicę wartości, po której można iterować dzięki forEach lub dzięki pętli for/of.
const person = {firstName: 'Adam', lastName: 'Kowalski', age: 30} Object.values(person).forEach(value=>console.log(value)) for (let value of Object.values(person)) { console.log(value) }Object.entries zwraca tablicę, której elementy to tablice zawierające w sobie pary klucz, wartość. W rezultacie możemy iterować za równo dzięki forEach jak również for/of. Poniżej kod pokazujący jak wygląda zwracana struktura.
const person = {firstName: 'Adam', lastName: 'Kowalski', age: 30} console.log(JSON.stringify(Object.entries(person)))Poniżej przykładowy kod z wykorzystaniem forEach oraz for/of
const person = {firstName: 'Adam', lastName: 'Kowalski', age: 30} Object.entries(person).forEach(([key, value]) => { console.log(`${key}: ${value}`) }) for (let [key, value] of Object.entries(person)) { console.log(`${key}: ${value}`) }Iterowanie po obiekcie jest teraz jeszcze łatwiejsze. Programiści mają do dyspozycji dwie dodatkowe metody, które robią to w podobny sposób jak Object.keys. Ważne jest aby pamiętać o podobieństwach:
- Iterowanie wyłącznie po własnych adekwatnościach, pomijane są adekwatności takie jak prototype,
- Taka sama kolejność elementów.
String padding
Dodane zostały dwie metody: padStart() oraz padEnd(). Obie metody zwracają ciąg o określonej długości, uzupełniając brakujące znaki wskazanym tekstem. Metody te przyjmują dwa parametry. Pierwszy parametr odpowiada za długość zwracanego stringa. Drugi parametr określa jakim tekstem będą wypełniane brakujące znaki. W przypadku nie podania drugiego parametru, brakujące znaki zostaną wypełnione spacjami. Przykładowy kod:
const string = 'code'.padStart(10) const string2 = 'code'.padStart(10, ':)') console.log(`${string} - ${string.length}`) console.log(`${string2} - ${string2.length}`)Różnica pomiędzy tymi metodami polega na tym, iż padStart dodaje określony tekst przed głównym stringiem, a metoda padEnd za głównym stringiem.
const string = 'code'.padStart(10) const string2 = 'code'.padStart(10, ':)') const string3 = 'code'.padEnd(10) const string4 = 'code'.padEnd(10, ':)') console.log(`${string} - ${string.length}`) console.log(`${string2} - ${string2.length}`) console.log(`${string3} - ${string3.length}`) console.log(`${string4} - ${string4.length}`)Object.getOwnPropertyDescriptors
Metoda ta zwraca własne deskryptory adekwatności obiektu. Przyjmuje jeden parametr, który odpowiada za przekazanie obiektu, z którego mają zostać pobrane deskryptory. Warto zapamiętać, iż zwraca wszystkie własne deskryptory obiektu w przeciwieństwie do metody Object.getOwnPropertyDescriptor, która odpowiada za zwrócenie wybranego deskryptora. W przypadku braku własnych adekwatności, zwracany jest pusty obiekt. Struktura zwracana przez metodę:
value – wartość danego atrybutu
writable – jeżeli zwraca true, to wartość może być zmieniona
get – funkcja, która służy jako getter
set – funkcja która służy jako setter
configurable – zwraca true, o ile typ może zostać zmieniony oraz adekwatność może zostać usunięta z obiektu,
enumerable – zwraca true, o ile adekwatności będzie dostępna podczas iteracji po adekwatnościach obiektu.
const person = {firstName: 'Adam', lastName: 'Kowalski', age: 30} const descriptors = Object.getOwnPropertyDescriptors(person); console.log(descriptors); console.log(descriptors.firstName);Trailing commas in function parameter lists and calls
Twórcy standardu postanowili ujednolicić zapis przecinków podczas deklaracji funkcji z zapisem tablic oraz obiektów literałowych. Przed ES2017 poprawnie utworzona funkcja wyglądała tak jak poniżej:
const print = function(a, b, c, d) { console.log(a, b, c, d) } print(1, 2, 3, 4)Od standardu ES8 dobrą praktyką jest dodawanie przecinka na końcu
const print = function(a, b, c, d, ) { console.log(a, b, c, d) } print(1, 2, 3, 4)Dzięki temu JS toleruje przecinki po ostatnim elemencie tablicy, po ostatniej wartości obiektu literałowego oraz po ostatnim argumencie deklarowanej funkcji. Poniżej przedstawiam przykład pokazujący omawiającą spójność zapisów.
const person = { firstName: 'Adam', lastName: 'Kowalski', age: 30, } const fruits = [ 'apple', 'bannana', ] const print = function(a, b, c, d, ) { console.log(a, b, c, d) } print(person, fruits, 1, 2)Async Functions
Jedną z największych zmian w ES8 było wprowadzenie funkcji asynchronicznych. We wcześniejszych wersjach JS w celu utworzenia funkcji asynchronicznej wykorzystywano bezpośrednio obiekt Promise, który został wprowadzony w ES6. Poniżej znajduję się przykład.
fetch('https://jsonplaceholder.typicode.com/todos') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error(error))Dzięki wprowadzeniu funkcji async/await możemy teraz powyższą funkcję zapisać zdecydowanie prościej. Poniżej przykład wykorzystania funkcji asynchronicznych.
async function fetchData () { try { const response = await fetch('https://jsonplaceholder.typicode.com/todos') const data = response.json() console.log(data) } catch(error) { console.error(error) } }Funkcję asynchroniczne mogą, ale nie muszą oczekiwać na asynchroniczne operacje oparte na obietnicach. W efekcie taka funkcja zawsze zwraca obietnice, którą można obsłużyć dzięki metod then, catch. Funkcje asynchroniczne pisane dzięki async/await wizualnie wyglądają jak kod synchroniczny i wykonywane są od góry do dołu. Zdecydowanie ułatwia to czytanie i rozumienie kodu, który wykonuje się podczas działania funkcji. Poniżej przykład z obsługą funkcji asynchronicznej dzięki metody then().
async function fetchData () { try { const response = await fetch('https://jsonplaceholder.typicode.com/todos') const data = response.json() console.log(data) return data } catch(error) { console.error(error) } } fetchData().then(response => console.log(response))Podsumowanie
ES2017 wprowadził kilka ciekawych rozwiązań. Najpopularniejszą nowością są funkcje asynchroniczne, które zdecydowanie poprawiają czytelność kodu asynchronicznego. Ustandaryzowanie przecinków, również pozytywnie wpływa na pracę z kodem, ponieważ nie trzeba się już zastanawiać czy pisząc deklarację funkcji można za ostatnim argumentem postawić przecinek czy też nie. Nowe iteratory obiektowe, pozwalają na wygodniejszą pracę podczas iterowania po adekwatnościach własnych obiektów. Wszystkie zmiany wprowadzone w 2017 roku pozytywnie wpływają na pracę z kodem oraz dają nowe możliwości programistom.