omówienie najważniejszych elementów
let a = 2;
let b = 3;
{
let b = 78;
console.log(a, b); // 2 78
}
console.log(a, b); // 2 3{
let a = 2;
var b = 3;
}
console.log(b); // 3
// error: a is not defined
console.log(a);for(let i = 0; i < 5; i++) {
console.log(i); // 0 1 2 3 4
}
//error: i is not defined
console.log(i);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 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 myName = "John";
const counter = 0;
counter++;
}
//TypeError: Assignment to constant variable.Stałe mają zakres blokowy!
{
const counter = 1;
const points = [200, 100];
counter++; // błąd
points = [300, 500]; // błąd
}{
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]
}console.log(points); // undefined
var points = 5;{
//ReferenceError: Cannot access
//'points' before initialization
console.log(points);
let points = 5;
}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.
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.
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"{
let a = 34;
console.log(a);
}
console.log(x);//ReferenceErrorZakres zmiennej let oraz const jest blokowy to znaczy:
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, i*1000);
}
//5
//5
//5
//5
//5Dlaczego?
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.
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
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
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:
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.
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,6callback - 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]);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.
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 } ]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).
arr.some() – sprawdź, czy jakikolwiek element spełnia dany warunek
arr.every() – sprawdź, czy wszystkie elementy spełniają dany warunek
//just function
const test = function(x) {
return 2 * x;
}
//arrow function :)
const test = (x) => 2 * x;//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;
}//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)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]function Counter() {
this.number = 0;
}
const x = new Counter();Mamy konstruktor Counter
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ę.
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)
function Counter() {
this.number = 0;
this.idInterval = setInterval( () => {
console.log(this); //Counter - yeaahhh
}, 1000);
}
const x = new Counter();naprawiamy
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
const person = {
age : "Jan",
getAge: () => {
//this to Window (w przeglądarce)
return this.age;
}
};
console.log(person.getAge());- w metodach obiektów
const button = document.querySelector('#music');
button.addEventListener('click', () => {
this.classList.toggle('on');
});- Callback functions z dynamicznym kontekstem
krótki zapis
zawsze anonimowe
nie mogą być konstruktorami
nie mają własności prototype
nie jest dla nich tworzone wyrażenie this
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;
}
}const tab = [2, 3, 4];
console.log(...tab);
// 2, 3, 4
const myName = "Agata";
console.log( [...myName] );
//["A", "g", "a", "t", "a"]const sum = (x, y, z) => {
return x + y + z;
}
const result = sum(...[2, 3, 4]);
console.log(result); //9const tab1 = [8, 9];
const tab2 = [1, 2, ...tab1, 3];
console.log(tab2); // [1, 2, 8, 9, 3]const test = (...points) => {
console.log(points);
}
test(1,2,3);
//[1, 2, 3]const test = (...points, p) => {
console.log(points);
}
//Error: Rest parameter must be last formal
//parameterconst 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' }Dane z obiektów i tablic możemy wyciągać poprzez tzw. przypisanie destrukturyzujące, czyli takie, które skraca zapis.
const arr = ["Azor", "Mruczek", "Reksio"];
const [a, b] = arr;
console.log(a, b) //"Azor", "Mruczek"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:
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)
const arr = ["Azor", "Mruczek", "Reksio"];
const [a, ...dogs] = arr;
console.log(a, dogs) //"Azor", ["Mruczek" , "Reksio"]Możemy też skorzystać z operatora rest
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a); // 2
console.log(b); // 1Zamiana wartości bez trzeciej zmiennej.
const dog = {
name : "Reksio",
owner : "Jan Kowalski",
age : 2
}
const { name, owner, age } = dog;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.
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.
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:
const city = `Warszawa.`;
const text = `Witaj ${city}`
console.log( text )
//"Witaj Warszawa"const a = 2;
const b = 3;
console.log(`Po dodaniu ${a} i ${b} mamy ${a+b}`);
//Po dodaniu 2 i 3 mamy 5console.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 oPodstawy
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:
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
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()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 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 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('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(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)
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)
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.