ECMAScript 6+
omówienie najważniejszych elementów
Zmienne
Zasięgi zmiennych

let
let - zakres blokowy
let a = 2;
let b = 3;
{
let b = 78;
console.log(a, b); // 2 78
}
console.log(a, b); // 2 3let - lepsze zarządzanie pamięcią
{
let a = 2;
var b = 3;
}
console.log(b); // 3
// error: a is not defined
console.log(a);let - a może w pętlach?
for(let i = 0; i < 5; i++) {
console.log(i); // 0 1 2 3 4
}
//error: i is not defined
console.log(i);let - pętle i deklaracja funkcji
var tab = [];
for(var i = 0; i < 4; i++) {
tab[i] = function() {
console.log(i);
}
}
tab[1](); // 4 Dlaczego ?
console.log(i); // 4Problem z var
let - pętle i deklaracja funkcji
let tab = [];
for(let i = 0; i < 4; i++) {
tab[i] = function() {
console.log(i);
}
}
tab[1](); // 1 Idealnie!!!
// Error: i is not defined
console.log(i);Rozwiązanie
const
const - deklaracja stałych
{
const myName = "John";
const counter = 0;
counter++;
}
//TypeError: Assignment to constant variable.Stałe mają zakres blokowy!
const - modyfikacja zmiennej czy zmiana jej wartości?
{
const counter = 1;
const points = [200, 100];
counter++; // błąd
points = [300, 500]; // błąd
}const - jeśli nie zmieniamy adresu zmiennej, to ok :)
{
const person = {name: "Ala"};
person.age = 21;
console.log(person); //{name: "Ala", age: 21}
const points = [100, 200, 300];
points.push(122);
console.log(points); //[100, 200, 300, 122]
}hoisting - var - przypomnienie
console.log(points); // undefined
var points = 5;hoisting - let i const
{
//ReferenceError: Cannot access
//'points' before initialization
console.log(points);
let points = 5;
}Temporal Dead Zone
Zasięg zmiennych
Rodzaje zakresów
Razem z ES6 dostaliśmy do dyspozycji możliwość deklaracji zmiennych w obrębie bloku kodu { }.
Wcześniej (w ES5) JavaScript tworzył zakres zmiennych tylko poprzez funkcje.
Rodzaje zakresów
Zmienne mogą być:
globalne – to zmienne nie zadeklarowane w żadnym zakresie lub zadeklarowane bez słowa kluczowego var.
Zmienne globalne są widoczne w całym naszym programie i są niszczone dopiero podczas zamknięcia okna przeglądarki.
lokalne – to zmienne zadeklarowane w zakresie funkcji przy pomocy słowa kluczowego var lub jeśli korzystamy z let lub const to w zakresie bloku {}
Zmienne lokalne są widoczne w zakresie funkcji, w której zostały stworzone oraz w funkcjach wewnętrznych. Są niszczone w chwili, w której funkcja je tworząca się kończy, wyjątek Domknięcia.
Zakres globalny i lokalny
var a = "global_a";
b = "global_b";
function myScope(){
var a = "local_a";
console.log(a); // "local_a"
console.log(b); // global_b
}
console.log(a); // "global_a"
console.log(b); // "global_b"Zakres blokowy
{
let a = 34;
console.log(a);
}
console.log(x);//ReferenceErrorZakres zmiennej let oraz const jest blokowy to znaczy:
Rodzaje zakresów - problem
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, i*1000);
}
//5
//5
//5
//5
//5Dlaczego?
Rodzaje zakresów - problem
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, i*1000);
}Zgodnie ze sposobem działania zakresu, wszystkie wywołania funkcji, mimo iż są zdefiniowane oddzielnie w poszczególnych iteracjach pętli, to jednak są umieszczone w tym samym współdzielonym zakresie globalnym, który tak naprawdę zawiera tylko jedną wersję zmiennej "i".
Potrzebujemy zatem zakresu tzw. domknięcia dla każdej iteracji.
Rodzaje zakresów - problem
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, i*1000);
})(i);
}
Użycie funkcji IIFE wewnątrz każdej iteracji spowodowało utworzenie nowego zakresu dla zmiennej. Należy oczywiście przekazać zmienną "i" do nowego zakresu:
//0
//1
//2
//3
//4
Rodzaje zakresów - problem
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, i*1000);
}
W poprzednim przykładzie do stworzenia zakresu stworzyliśmy funkcję, ale tak naprawdę potrzebujemy zakresu bloku. Zmienna "i" zostanie zadeklarowana dla każdej iteracji:
//0
//1
//2
//3
//4
Domknięcie
function testClousure(name) {
setTimeout(function noName() {
console.log("hello ", name);
}, 1000);
}
testClousure("Agata");
W poprzednim przykładzie korzystaliśmy z IIFE, aby stworzyć nowy zakres (domknięcie) dla zmiennej "i". Czym było to domknięcie? Spójrz na poniższy przykład:
Domknięcie
function testClosure(name) {
setTimeout(function noName() {
console.log("hello ", name);
}, 1000);
}
testClosure("Agata");
Funkcja noName() zostaje przekazana funkcji setTimeout.
Zakres funkcji noName() (Zakres domknięcia) jest stworzony za pomocą funkcji testClosure(), dzięki temu funkcja noName() może używać zmiennej name.
1s po wykonaniu funkcji testClosure() jej zakres wewnętrzny powinien zostać usunięty, ale funkcja noName() wciąż ma domknięcie poprzez ten zakres.
Metody tablicowe ES6
map
map to metoda, która jest wywoływana na elementach tablicy.
Różnica pomiędzy forEach a map jest taka, że map zwraca nową tablicę.
const arr = [1,2,3];
const newArr = arr.map((el) => {
return el*2;
})
console.log(newArr); //1,4,6map - parametry
callback - funkcja wykonywana dla każdego elementu tablicy przyjmuje parametry:
-
currentValue - bieżąca wartość - wymagany parametr
-
index - indeks bieżącego elementu tablicy - opcjonalny
-
array - odniesienie do obiektu, na którym wywołujemy funkcję - opcjonalny
-
thisArg - dodajemy w callbacku, ustawiana wartość zmiennej this - opcjonalny
arr.map(callback(currentValue[, index[, array]]) {
// execute something
}[, thisArg]);filter
Metoda filter (jest bardzo podobna do map().
Różnicą jest to, że filter() zwraca nową tablicę z wartościami, które w wyniku działania warunku są prawdą, np. value >= 18 (chcemy zostawić wartości większe lub równe 18).
W przypadku tej metody, nie możemy poddawać modyfikacji tych elementów.
filter
const people = [
{name: "John", age: 23},
{name: "Ann", age: 16}
];
const adults =
people.filter(function(person) {
return person.age >= 18;
});
console.log(adults);
// [ { name: 'John', age: 23 } ]reduce
const array1 = [1, 2, 3];
const sum =
array1.reduce(function(total, item) {
return total + item;
});
console.log(sum); // 6
Wywołaj funkcję dla każdego z elementów w taki sposób, żeby mieć dostęp do wartości otrzymanej w wyniku wykonania funkcji dla poprzedniego elementu (przydatne przy sumowaniu).
Inne nowe funkcje interacyjne
-
arr.some() – sprawdź, czy jakikolwiek element spełnia dany warunek
-
arr.every() – sprawdź, czy wszystkie elementy spełniają dany warunek
Arrow function
Arrow function
//just function
const test = function(x) {
return 2 * x;
}
//arrow function :)
const test = (x) => 2 * x;arrow function - różne zapisy
//Bez parametrów - trzeba nawias
const test = () => 2;//1 parametr - nie trzeba nawiasu
const test = x => x * 2;
//1 parametr + ciało funkcji
const test = x => {
let a = 4;
return x * a;
}arrow function - różne zapisy
//Kilka parametrów - trzeba nawias
const sum = (x, y) => x + y; //Kolejna linijka
const sum = (x, y) => (
x + y
)//Immediately Invoked Function Expression
( (x) => x * 2 ) (5)arrow function - map, filter
const points = [10, 25, 30];
const more = points.map(p => p * 2);
console.log(more); // [20, 50, 60]const points = [10, 25, 30];
const moreThan20 = points.filter(p => p > 20);
console.log(moreThan20); // [25, 30]Przykład this
function Counter() {
this.number = 0;
}
const x = new Counter();Mamy konstruktor Counter
Przykład this
function Counter() {
this.number = 0;
this.idInterval = setInterval(function inc() {
this.number++;
console.log(this.number);
}, 1000);
}
const x = new Counter();Zwiększamy licznik o 1 co sekundę.
Przykład this
function Counter() {
this.number = 0;
this.idInterval = setInterval(function inc() {
console.log(this); //Window !!!
}, 1000);
}
const x = new Counter();this to Window (w przeglądarce)
arrow function - this
function Counter() {
this.number = 0;
this.idInterval = setInterval( () => {
console.log(this); //Counter - yeaahhh
}, 1000);
}
const x = new Counter();naprawiamy
arrow function - this
function Counter() {
this.number = 0;
this.idInterval = setInterval(() => {
this.number++;
console.log(this.number);
}, 1000);
}
const x = new Counter();naprawiamy
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16
arrow function - kiedy nie używać?
const person = {
age : "Jan",
getAge: () => {
//this to Window (w przeglądarce)
return this.age;
}
};
console.log(person.getAge());- w metodach obiektów
arrow function - kiedy nie używać?
const button = document.querySelector('#music');
button.addEventListener('click', () => {
this.classList.toggle('on');
});- Callback functions z dynamicznym kontekstem
Arrow function
-
krótki zapis
-
zawsze anonimowe
-
nie mogą być konstruktorami
-
nie mają własności prototype
-
nie jest dla nich tworzone wyrażenie this
Zadania
Klasy
class Vehicle {
constructor(type) {
this.type = type;
}
getType() {
return this.type;
}
}
class Car extends Vehicle {
constructor(type, age) {
super(type);
this.age = age;
}
}
let bmw = new Car("BMW", 4);
console.log(bmw);
class Calculator{
constructor(x, y) {
this.x = x;
this.y = y;
}
add() {
return this.x * this.y;
}
}
class MyCalculator extends Calculator{
constructor(x, y, z) {
super(x, y);
this.z = z;
}
multiply() {
return super.add() * this.z;
}
}Super
Rest / spread operator
and properties
Operator rozproszenia
(spread operator)
const tab = [2, 3, 4];
console.log(...tab);
// 2, 3, 4
const myName = "Agata";
console.log( [...myName] );
//["A", "g", "a", "t", "a"]Operator rozproszenia
(spread operator)
const sum = (x, y, z) => {
return x + y + z;
}
const result = sum(...[2, 3, 4]);
console.log(result); //9Operator rozproszenia
(spread operator)
const tab1 = [8, 9];
const tab2 = [1, 2, ...tab1, 3];
console.log(tab2); // [1, 2, 8, 9, 3]Operator reszty
(rest operator)
const test = (...points) => {
console.log(points);
}
test(1,2,3);
//[1, 2, 3]Operator reszty
(rest operator) - ograniczenie
const test = (...points, p) => {
console.log(points);
}
//Error: Rest parameter must be last formal
//parameterRest / Spread properties
const person = {
firstName: 'Jan',
lastName: 'Nowak',
country: 'Poland',
city: 'Warsaw',
};
const { firstName, lastName, ...rest } = person;
console.log(firstName); // Agata
console.log(lastName); // Malec
console.log(rest); // { country: 'Poland', city: 'Warsaw' }Destrukturyzacja
Dane z obiektów i tablic możemy wyciągać poprzez tzw. przypisanie destrukturyzujące, czyli takie, które skraca zapis.
Destrukturyzacja
Destrukturyzacja tablic
const arr = ["Azor", "Mruczek", "Reksio"];
const [a, b] = arr;
console.log(a, b) //"Azor", "Mruczek"Destrukturyzacja tablic
const arr = ["Azor", "Mruczek", "Reksio"];
const [a, ,c] = arr;
console.log(a, c) //"Azor", "Reksio"Jeśli chcemy pominąć jakąś wartość, to używamy przecinka:
Destrukturyzacja tablic
const arr = ["Azor", "Mruczek"];
const [a="nie ma", b="nie ma", c="nie ma"] = arr;
console.log(a, b, c) //"Azor", "Mruczek" , "nie ma"Możemy ustawić zmiennych wartości domyślne ( w przypadku braku takiej wartości w tablicy)
Destrukturyzacja tablic
const arr = ["Azor", "Mruczek", "Reksio"];
const [a, ...dogs] = arr;
console.log(a, dogs) //"Azor", ["Mruczek" , "Reksio"]Możemy też skorzystać z operatora rest
Destrukturyzacja tablic
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a); // 2
console.log(b); // 1Zamiana wartości bez trzeciej zmiennej.
Destrukturyzacja obiektów
const dog = {
name : "Reksio",
owner : "Jan Kowalski",
age : 2
}
const { name, owner, age } = dog;Destrukturyzacja obiektów
const dog = {
name : "Reksio",
owner : "Jan Kowalski"
}
const {
name = "Azor",
owner = "nie ma",
age = "brak danych"
} = dog;Jeśli w obiekcie nie będzie jakiejś wartości zostanie zwrócony undefined. Możemy tego uniknąć również stosując wartości domyślne.
Destrukturyzacja obiektów
const dog = {
name : "Reksio",
owner : "Jan Kowalski"
}
const {
name: dogName = "Azor",
owner = "nie ma",
age = "brak danych"
} = dog;Jeśli chcemy stworzyć zmienne o innych nazwach niż klucze obiektu, używamy dwukropka.
Destrukturyzacja i funkcje
const getData = ({name, owner}) => {
console.log(name, owner)
}
const dog = {
name : "Reksio",
owner : "Jan Kowalski"
}
getData(dog)const getData = (obj) => {
console.log(obj.name, obj.owner)
}
const dog = {
name : "Reksio",
owner : "Jan Kowalski"
}
getData(dog)zamiast:
mogę tak:
Template string
Template string
const city = `Warszawa.`;
const text = `Witaj ${city}`
console.log( text )
//"Witaj Warszawa"Template string
const a = 2;
const b = 3;
console.log(`Po dodaniu ${a} i ${b} mamy ${a+b}`);
//Po dodaniu 2 i 3 mamy 5Template string
console.log(`Ja uwielbiam ją
ona tu jest
i tańczy dla mnie
o o o o
`);
//Ja uwielbiam ją
//ona tu jest
//i tańczy dla mnie
//o o o oPromises &
Async / Await
Podstawy
Promises
Przykład: dziadek obiecał nam Ferrari (złożył obietnicę), ale nie powiedział kiedy nam go kupi.
Promise może być w jednym z 3 stanów:
- Oczekujący (pending) - jeszcze nie wiemy czy dziadek dotrzyma obietnicy czy nie.
- Rozwiązany (resolved) - jeździmy super furą,
- Odrzucony (rejected) - to chyba jakiś żart , a nie obietnica! ;)
Promises
const getCar = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Ferrari!');
}, 2000);
});
}
const promisedCar = getCar(); // zwraca obiekt typu Promise
promisedCar
.then(car => console.log('Kocham dziadka za ', car))
.catch(error => console.log('Dziadek żartował', error));Jeśli obietnica zostanie spełniona wykona się kod w funkcji then, w przeciwnym wypadku z funkcji catch. Niestety nie wiemy dokładnie kiedy to nastąpi, może nastąpić w ciągu 1 sekundy, a może nastąpić za miesiąc lub rok
Async / Await
Słowo async - będzie oznaczać funkcje jako asynchroniczną (czyli taką, która korzysta z Promises)
Słowo await - będzie sprawiało, że wywołania Promises będą sekwencyjne - synchroniczne
const getCar = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Ferrari!');
}, 2000);
});
}
const waitForCar = async () => {
const car = await getCar();
console.log("Mam" , car);
}
waitForCar()Async / Await - try .. catch
const getCar = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('Nie ma auta');
}, 2000);
});
}
const waitForCar = async () => {
try {
const car = await getCar();
console.log(car);
} catch(error) {
console.log(error);
}
}
waitForCar();Fetch API
Fetch API
Fetch Api to narzędzie, dzięki któremu możemy komunikować się z różnymi źródłami danych, podobnie jak w przypadku XMLHttpRequest. Różnica między nimi polega na tym, że Fetch API korzysta z tzw. Promises, dzięki temu unikamy kodu spaghetti.
Korzystanie z fetch API jest także dużo prostsze ( nie trzeba pamiętać o wielu rzeczach tak jak w przypadku XMLHttpRequest).
Fetch API
Fetch Api dostarcza nam odpowiedni interfejs do wykonywania zapytań i odbierania odpowiedzi.
Mamy tutaj do dyspozycji funkcję fetch(), dzięki, której w prosty sposób możemy wykonywać asynchroniczne zapytania.
fetch('https://pokeapi.co/api/v2/pokemon/pikachu')
.then(response => response.json())
.then(data => console.log(data));Najprostsze użycie fetch'a. Przyjmuje on jeden argument - ścieżkę do zasobu, który chcemy pobrać. Zwraca on promise, który zawierający odpowiedź (obiekt Response).
Fetch API
fetch('https://pokeapi.co/api/v2/pokemon/pikachu')
.then(response => response.json())
.then(data => console.log(data));obiekt Response (to odpowiedź HTTP, nie plik JSON, dlatego musimy go najpierw wydobyć z tego obiektu używając funkcji json()
Fetch API
fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache,
//only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer',
//// no-referrer, *no-referrer-when-downgrade, origin,
//origin-when-cross-origin,
//same-origin, strict-origin,
//strict-origin-when-cross-origin, unsafe-url
body: JSON.stringify(data) // body data type must match
//"Content-Type" header
});Funkcja fetch przyjmuje jeszcze jeden parametr (opcje)
Wysyłanie danych
const data = { user: 'Agata' };
fetch('https://example.com/profile', {
method: 'POST', // or 'PUT'
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});Funkcja fetch przyjmuje jeszcze jeden parametr (opcje)
async/await
const getData = async (url) => {
const r = await fetch(url);
const data = await r.json();
console.log(data);
}
getData('https://pokeapi.co/api/v2/pokemon/pikachu');Funkcja fetch możemy użyć korzystając z async/await.
ECMAScript 6+ przypomnienie
By noinputsignal
ECMAScript 6+ przypomnienie
- 2