JavaScript 14.11
Obsługa błędów
Amatorski kod od profesjonalnego różni się tym jak bardzo dbamy o użytkownika, tym jak obsługujemy błędy, żeby się nie wykopyrtło
Expect unexpected , kod defensywny
Try { } Catch {} sprawdza czy taka operacje da się wykonać, jeśli się da to robi, jak nie to wywołuje się error, pokazuje użytkownikowi co się wykopyrtło np.
- błędy które wynikają z nieprawidłowych danych podanych przez user
- operacja się nie wykonała bo np. nie było zasobów json czy plik który parsujemy ma nieprawidłowy format
- zasoby nie istnieją
Nie ma sensu łapać błędów try catch których nie umiemy obsłużyć.
Lepiej zrobić try catch na np. dzieleniu samym jeśli np. użytkownik podał 0 to dopiero w samym dzieleniu to się sprawdza
W JS dzielenie przez 0 nie wywołuje błędu, wywołuje NaN
Czy błąd był czy nie, to try {} catch {} finally{} i to finally zawsze się wykona, np. próbujemy otworzyć plik, bleble i chcemy zamknąć posprzątać i to finally to np zwolnienie zasobów, zamkniecie plików, zaktualizowanie stanu, i zawsze się ten kod wykona
reference error próba użycia nie istniejącej zmienne
type error operacje na niewłaściwym typie np. metoda na null a wywołujemy nie funkcje
syntax error błąd składni kodu
range error np. ujemy indeks dla indeksu tablicy, za głęboka rekurencja, prób zapisania wartości większej niż zakres inta
try{
const obiekt = null;
console.log(obiekt.wlasciwosc); // To wywoła TypeError
} catch (error) {
console.error("Wystąpił błąd:", error.message);
}
console.log("Program kontynuuje działanie po obsłudze błędu.");
// Try-catch-finally
// Finally wykonuje się zawsze
let zasob = false;
try {
console.log("Otwieranie zasobu...");
zasob = true;
// Symulacja operacji która może się nie udać
if (Math.random() < 0.5) {
throw new Error("Operacja nie powiodła się");
}
console.log("Operacja zakończona sukcesem");
} catch (error) {
console.log("Błąd: " + error.message);
} finally {
// Ten blok ZAWSZE się wykona
if (zasob) {
console.log("Zamykanie zasobu...");
zasob = false;
}
console.log("Finally: Sprzątanie zakończone");
}
This
referencja do kontekstu, do obiektu w którym funkcja jest wykonywana, kontekst może się zmienić czy funkcja została wykonana jako metoda obiektów, konstruktor, istnieje kilka różnych kontekstów
- kontekst globalny zmienna do której przypisuje wartość this, tak po prostu, jeżeli wywołujemy taka zmienna/wartość bez żadnego dodatkowego kontekstu console.log obiekt window który opisuje okienko przeglądarki, w kontekscie globalnym jeżeli wywołam this to dostane window, bo jak porównuje bez konwersji typów czy this jest window to jest
- funkcji jeżeli wołam this w funkcji i nie mam dodatkowego kontekstu, wartości, nic nie przekazuje jako argument to również dostaje window bo this się nie zmieniło
- this stritct mode przełącznik mówi w takim trybie ma działać funkcja, nie da się wyciągać this z kontekstu globalnego undefined
- funkcja zagnieżdżone wnętrza i zewnętrza daje window bo dalej każda z nich posługuje się kontekstem globalnym, nie daliśmy żadnego kontekstu wiec nie działają jako globalny, window lub undefined
- callback przekazanie funkcji do funkcji globalnie pokazuje window ale jako przekazaliśmy cos do funkcji to pokazuje nam ta funkcje
- obiekt można przypisac this do zmiennej i moze byc przechowywana w obiekcie, wywołujemy ją z wewnątrz obiektu pokazuje nazwę obiektu, zapisywanie do obiektu jest średnio czytelne ale poprawne
- arrow function
This zależy od wywoływania a nie definicji
- jak nie przekazujemy żadnej wartości = window, kontekst globalny
- przypisany jako metoda opakowany w obiekt dostaniemy ten obiekt/wskazuje obiekt
- jeżeli funkcje są zagnieżdżone to potrafią wrócić do kontekstu globalnego, dlatego tworząc metody takie to trzeba uważać
Najczęstszy problem - tracimy kontekst this jeśli przekazujemy callbacks lub przypisujemy je do obiektu, metoda oderwana od obiektu
This jest istotny, wykorzystywany podczas tworzenia metod do obiektów specjalne funkcje metody aby obsługiwać dane obiektu, kontekst dla METOD jest istotny
- this w metodzie obiektu: jeśli wywołuje this.wiek this.imie zamiast uzytkownik.imie uzytkownik.wiek mogę ta metodę przypisać dla użytkownik 1 użytkownik 2 itd wiec ta metoda jest nie tylko dla tego jednego
- method shorthand stara miała słowo function, w ES6 nie trzeba podawać function, ale nowa działa jak stara, można pisać function, nie trzeba
- this wskazuje na obiekt przed kropką, mam dwa obiekty u1 u2 i obu daje ta sama metodę i dostaje się kontekst każdego z obiektów osobno
- ... utrata kontekstu
- bind przeglądarko nie trać kontekstu to nadal ma być ten obiekt
- Method Chaining Można wykorzystać więcej niż 1 metodę w obiekcie np. kalkulator, tworzymy łańcuch z metod które obiekt ma (kalkulator)
- getter setter dodatkowe metody, maja one za zadanie zarządzać pewnym stanem. Getter i Setter to są metody które używają dodatkowych słów kluczowych get/set, są wywoływane jako właściwości, ale wykonują kod, this wewnątrz nich odnosi sie/wskazuje na obiekt jak w normalnych metodach. Takie metody z automatu wyliczają i zwracają wartość
// 1. THIS W METODZIE OBIEKTU
const uzytkownik = {
imie: "Jan",
wiek: 30,
przedstawSie: function() {
console.log("Cześć, jestem", this.imie); // uzytkownik.imie
console.log("Mam", this.wiek, "lat");
return this.imie;
}
};
const imie1 = uzytkownik.przedstawSie();
console.log("Zwrócone imię:", imie1);
// 2. METHOD SHORTHAND (ES6)
const produkt = {
nazwa: "Laptop",
cena: 3000,
// Stara składnia
pokazStara: function() {
return this.nazwa + ": " + this.cena + " zł";
},
// Nowa składnia - bez function
pokazNowa() {
return this.nazwa + ": " + this.cena + " zł";
}
};
console.log(produkt.pokazStara());
console.log(produkt.pokazNowa());
// 3. THIS wskazuje na obiekt PRZED KROPKĄ
function pokazNazwe() {
return this.nazwa;
}
pokazNazwe(); // undefined (this to global/window)
const obiekt1 = { nazwa: "Obiekt 1", metoda: pokazNazwe };
const obiekt2 = { nazwa: "Obiekt 2", metoda: pokazNazwe };
console.log("Obiekt1:", obiekt1.metoda()); // "Obiekt 1"
console.log("Obiekt2:", obiekt2.metoda()); // "Obiekt 2"
// 4. PROBLEM - utrata kontekstu
const osoba = {
imie: "Anna",
powitaj: function() {
console.log("Witaj,", this.imie);
}
};
osoba.powitaj(); // Działa - "Witaj, Anna"
const oddzielnaMetoda = osoba.powitaj;
// oddzielnaMetoda(); // BŁĄD! this to undefined/window
// 5. ROZWIĄZANIE - bind (omówimy za chwilę)
const zwiazanaMetoda = osoba.powitaj.bind(osoba);
zwiazanaMetoda(); // Działa - "Witaj, Anna"
// 6. METHOD CHAINING - zwracanie this
const kalkulator = {
wartosc: 0,
dodaj(n) {
this.wartosc += n;
return this; // Zwraca obiekt dla chaining
},
odejmij(n) {
this.wartosc -= n;
return this;
},
pomnoz(n) {
this.wartosc *= n;
return this;
},
pobierz() {
return this.wartosc;
}
};
const wynik = kalkulator
.dodaj(10)
.pomnoz(2)
.odejmij(5)
.pobierz();
console.log("Wynik chaining:", wynik); // 15
// 7. GETTER I SETTER
const prostokat = {
szerokosc: 10,
wysokosc: 5,
get pole() {
return this.szerokosc * this.wysokosc;
},
set pole(nowePole) {
// Proporcjonalne skalowanie
const wspolczynnik = Math.sqrt(nowePole / this.pole);
this.szerokosc *= wspolczynnik;
this.wysokosc *= wspolczynnik;
},
get obwod() {
return 2 * (this.szerokosc + this.wysokosc);
}
};
console.log("Pole:", prostokat.pole); // 50
console.log("Obwód:", prostokat.obwod); // 30
prostokat.pole = 200;
console.log("Nowe wymiary:", prostokat.szerokosc, prostokat.wysokosc);
// 8. METODY WYWOŁUJĄCE INNE METODY
const gracz = {
imie: "Gracz1",
punkty: 0,
dodajPunkty(ile) {
this.punkty += ile;
this.sprawdzPoziom();
},
sprawdzPoziom() {
if (this.punkty >= 100) {
console.log(this.imie, "osiągnął poziom PRO!");
}
},
reset() {
this.punkty = 0;
console.log(this.imie, "zresetowany");
}
};
gracz.dodajPunkty(50);
gracz.dodajPunkty(60);
gracz.reset();
// 9. THIS w metodach zagnieżdżonych obiektów
const firma = {
nazwa: "TechCorp",
dział: {
nazwa: "IT",
pokazInfo() {
console.log("Dział:", this.nazwa);
// this to dział, NIE firma!
}
}
};
firma.dział.pokazInfo(); // "Dział: IT"
// 10. Dostęp do zewnętrznego this
const organizacja = {
nazwa: "Organizacja",
dział: {
nazwa: "Marketing",
pokazInfo: function() {
console.log("Dział:", this.nazwa);
// Aby dostać się do organizacji, potrzebujemy referencji
// Można przekazać przez parametr lub zapisać wcześniej
}
}
};
// 11. Praktyczny przykład - zarządzanie stanem
const formularz = {
dane: {
imie: "",
email: ""
},
ustawPole(pole, wartosc) {
this.dane[pole] = wartosc;
return this;
},
waliduj() {
const bledy = [];
if (!this.dane.imie) {
bledy.push("Imię wymagane");
}
if (!this.dane.email || !this.dane.email.includes("@")) {
bledy.push("Email niepoprawny");
}
return bledy;
},
wyslij() {
const bledy = this.waliduj();
if (bledy.length > 0) {
console.log("Błędy:", bledy);
return false;
}
console.log("Wysyłam:", this.dane);
return true;
}
};
formularz
.ustawPole("imie", "Jan")
.ustawPole("email", "[email protected]")
.wyslij();
// Wyświetlenie
document.getElementById("methodContext").innerHTML =
"Użytkownik: " + imie1 + "<br>" +
"Produkt: " + produkt.pokazNowa() + "<br>" +
"Kalkulator chaining: " + wynik + "<br>" +
"Pole prostokąta: " + prostokat.pole.toFixed(2);Tablice
Przechowują elementy różnych typów w jednej tablicy. Zawsze mają długość.
- Literał wypisać tablice [1,2,3,4,5]; konkretnie wypisane elementy tablicy, automatyczny indeks, podane wartości, tablica może byc pusta, nie musi miec wartości pustaTablica[]; może mieć różne typy liczba satring boolean obiekt tablica funkcja WPISUJEMY ELEMENTY TABLICY
- Konstruktor new Array mówimy nowa tablica i podajemy wartości, ale tez nie musimy podawać wartości, podobnie jak literały, pozwala na podanie argumentów, można np parse string i z tego parse zrobi new array
- Wykorzystać długość tablicy np stworzyć tablice rozmiarTablicy = newArray(5), można zmienna przerobić na tablice, można potem dorzucić elementy i mogę wskazać długość jaka ma być, ta tablica ma mieć długość 5
Jest problem konstruktora - w ten sposób nie tworze tablicy z 1 elementem, tylko tablice o 5 miejscach, wtedy robi się string
Array.of() Metoda w tablicy rozwiązująca ten problem: Array.of(5) tablica z podanych argumentów, elementy są string
Array.from() konwersja ze stringa zamienia np Hello na odpowiednio 1 2 3 ...
Obiekt na tablice - za pomocą metody from mogę z obiektu zrobić array (parsować) CAŁKOWITA KONWERSJA TYPU
Sklejanie tablicy kopia tablicy js5.html
To wyżej to były tablice jednowymiarowe
Nie da się wprost stworzyć tablicy wielowymiarowej, ale da się tablice tablic MACIERZ, tablica 2d
możemy sami ustalić długość tablicy bez konstruktora mojaTablica.length =3;
możemy sami wypełnić tablice elementami np. o długości 50, same 0 mojaTablica.fill(0);
Możemy zapisywać pojedynczo tablice, przy zmianach np. przy każdym razie jak operacja się wydarzy, jeśli obliczenie się nie wykona, program się wywróci to mamy często tablicy zapisana i jesteśmy w stanie ja np. przywrócić
// 1. LITERAŁ TABLICY - najpopularniejszy sposób
const liczby = [1, 2, 3, 4, 5];
console.log("Literał:", liczby);
const owoce = ["jabłko", "banan", "pomarańcza"];
console.log("Owoce:", owoce);
const pustaTablica = [];
console.log("Pusta tablica:", pustaTablica);
const mieszana = [
42, // liczba
"tekst", // string
true, // boolean
{ klucz: "wartość" }, // obiekt
[1, 2, 3], // tablica
function() { return "fn"; } // funkcja
];
console.log("Mieszana:", mieszana);
console.log("Element funkcja:", mieszana[5]());
// Kontruktor Tablicy
const tablicaKonstruktor = new Array(10, 20, 30, 40, 50);
console.log("Konstruktor:", tablicaKonstruktor);
const pustaKonstruktor = new Array();
console.log("Pusta konstruktor:", pustaKonstruktor);
// Tworzenie tablicy o określonym rozmiarze
const rozmiarTablicy = new Array(5); // tablica z 5 pustymi miejscami
console.log("Tablica o rozmiarze 5:", rozmiarTablicy);
console.log("Długość tablicy:", rozmiarTablicy.length);
const tablicaJedenEl = new Array("5");
console.log("Tablica z jednym elementem '5':", tablicaJedenEl);
// Array.of() - tworzy tablicę z podanych argumentów
const of1 = Array.of(5); // [5]
const of2 = Array.of(1, 2, 3); // [1, 2, 3]
console.log("Array.of(5):", of1);
console.log("Array.of(1,2,3):", of2);
//Array.from() - konwersja ze stringa
const znaki = Array.from("Hello");
console.log("Z stringa:", znaki);
const arrayLike = { 0: "a", 1: "b", 2: "c", length: 3 };
const prawdziwa = Array.from(arrayLike);
console.log("Z array-like:", prawdziwa);
const oryginalna = [1, 2, 3];
const oryginalna2 = [1, 2, 3];
const kopia = [...oryginalna, ...[4, 5, 6], ...oryginalna2]; // spread operator
console.log("Kopia:", kopia);
// kopie[0]
// Macierz 2D
const macierz2D = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
console.log("Macierz 2D:", macierz2D);
console.log("Element [1][2]:", macierz2D[1][2]); // 6
console.log("Element [1][2]:", macierz2D[2][1]);
const mojaTablica = [];
mojaTablica.length = 50; // Ustawienie długości tablicy na 50
console.log("Moja tablica o długości 3:", mojaTablica);
mojaTablica.fill(0); // Wypełnienie zerami
console.log("Moja tablica po wypełnieniu zerami:", mojaTablica);
// Dni tygodnia
const dniTygodnia = [null,
"Poniedziałek", "Wtorek", "Środa",
"Czwartek", "Piątek", "Sobota", "Niedziela"
];
console.log("Dni tygodnia:", dniTygodnia);
const config = [
{ apiUrl:"https://api.com" },
{ timeout: 5000 },
{ retries: 3 }
];
const uzytkownicy = [
{ id: 1, imie: "Jan", wiek: 30 },
{ id: 2, imie: "Anna", wiek: 25 },
{ id: 3, imie: "Piotr", wiek: 35 }
];
console.log("Użytkownicy:", uzytkownicy);Tablice na co dzień
- tam, gdzie potrzebujemy uporządkowane wartości np. dni tygodnia, łatwo do takiej tablicy sięgnąć, można wsadzić null żeby liczyło od 1 nie od zera BO ZAWSZE JEST OD ZERA
- domyślne konfiguracje, tablica może przechowywać dowolne elementy wiec szczególności często przechowuje konfiguracje bo mamy łatwy dostęp do danego elementu, będzie wyglądać jak obiekt, TABLICE OBIEKTÓW
Jak wygląda tablica przechowująca wszystkie treści na stronie?
Zaprojektuj tablicę która pozwoli na przechowywanie treści strony typu SPA uwzględnij możliwe elementy pojedynczego artykułu/ sekcji: TABLICA OBIEKTÓW, zadeklarować zmienna w której jest obiekt. CZEMU TO JEST DOBRE: pseudo powtarzalne struktury które maja tylko wyplenienie danymi, możemy dodawać, usuwać elementy za pomocą standardowych metod tablicowych
const tresciStrony = [
{ id: 1,
section: "header",
title: "Strona Główna",
content: "Witamy na naszej stronie!",
author: "Jan Kowalski",
creationDate: "2024-01-15",
isActive: true
},
{ id: 2,
section: "about",
title: "O Nas",
content: "Jesteśmy firmą zajmującą się...",
author: "Anna Nowak",
creationDate: "2024-02-20",
isActive: true
}
];
console.log(tresciStrony);Metody na dodawanie usuwanie
- Push - dodaj na koniec i zwraca nowa długość, to oznacza, ze my wiemy na którym indeksie dodaliśmy ten element, push potrafi przyjmować więcej niż 1 argument i wszystkie zostaną dodane. SZYBKI, nie modyfikuje całej tablicy
- Pop - usuwa z końca tablicy elementy, jeżeli taka tablica ma jakies elementy, to pop usuwa ostatni element i zmniejsza o 1 długość tablicy i w ten sposob modyfikuje jej zawartość. Jeśli tablica jest pusta, zwraca wartość undefined
- Shift - usuwa pierwszy element tablicy i zwraca indeks, wszystkie są przesuwane w lewo czyli indeksy dla pozostałych elementów są zmniejszane o jeden CZEMU SHIFT JEST WOLNIEJSZA bo musimy wszyyystko przerobić
- Unshift - przesuwamy elementy w druga str i na początku tablicy dodajemy nowy element, może przyjąć więcej niż 1 argument tak samo jak pusz, możemy dodać więcej niż 1 element na raz, WOLNIEJSZY od push
Lepiej tablice podzielić i przeprowadzić operacje na jej końcu i ja znów złączyć niż wykonywać operacje na wszystkich elementach.
Tablica ze zdarzeniami, log wewnętrzy i każda operacja może być z odpowiednimi dodatkowymi metadanym zapisana do tablicy/tablicy obiektów aby uzyskać takie cos
// 1. PUSH - dodaj na koniec
const owoce = ["jabłko", "banan"];
console.log("Przed push:", owoce);
const nowaDlugosc = owoce.push("pomarańcza");
console.log("Po push:", owoce);
console.log("Nowa długość:", nowaDlugosc); // 3
// Push wielu elementów
owoce.push("gruszka", "śliwka");
console.log("Po wielu push:", owoce);
// 2. POP - usuń z końca
const warzywa = ["marchew", "brokuł", "pomidor"];
console.log("Przed pop:", warzywa);
const usuniety = warzywa.pop();
console.log("Usunięty:", usuniety); // "pomidor"
console.log("Po pop:", warzywa);
// Pop z pustej tablicy
const pusta = [];
const usunPusty = pusta.pop();
console.log("Pop z pustej:", usunPusty); // undefined
// 3. SHIFT - usuń z początku
const liczby = [1, 2, 3, 4, 5];
console.log("Przed shift:", liczby);
const pierwszy = liczby.shift();
console.log("Usunięty pierwszy:", pierwszy); // 1
console.log("Po shift:", liczby);
// 4. UNSHIFT - dodaj na początek
const kolory = ["zielony", "niebieski"];
console.log("Przed unshift:", kolory);
const nowaDl = kolory.unshift("czerwony");
console.log("Po unshift:", kolory);
console.log("Nowa długość:", nowaDl);
// Unshift wielu elementów
kolory.unshift("fioletowy", "żółty");
console.log("Po wielu unshift:", kolory);
// Push jest szybszy - dodaje na koniec
// Unshift jest wolniejszy - musi przesunąć wszystkie elementy
const duza = [];
console.time("push 10000");
for (let i = 0; i < 10000; i++) {
duza.push(i);
}
console.timeEnd("push 10000");
const duza2 = [];
console.time("unshift 10000"); // Mniej bo wolniejsze
for (let i = 0; i < 10000; i++) {
duza2.unshift(i);
}
console.timeEnd("unshift 1000");REFERENCJE DO TABLICY
js8
Stworze tablice, na jej podstawie stworze druga to będzie to jedna i ta sama tablica, jak kopiujemy to nie tworzy nowej tylko odwołuje się do tego co jest w pamięci, NADPISUJEMY ORYGINAŁ
Jeśli chcemy zachować oryginał niezmieniony to nie przez przypisanie a przez kopiowanie
Jeśli potrzebujemy przeprowadzić operacje na wszystkich elementach tablicy możemy walnąć pętle w których możemy dodawać usuwać modyfikować itd
Tablica dostaje nazwy kluczy automatycznie i ma length. To specjalny obiekt. Ale to dalej oznacza ze jak zrobimy typeof to dostajemy obiekt to jak sprawdzić czy cos tablica jest? .isArray metoda wbudowana w konstruktor array, jesteśmy w stanie za jego pomocą sprawdzić czy tablica jest tablica
Trzeba to sprawdzić żeby można było użyć metod tablicowych map shift push pop itd
Funkcją waliduje czy ten argument który dostaje jest tablica, jeżeli ta tablica nie jest to tworzy nowe zdarzenie "błąd" i skoro funkcja tworzy takie zdarzenie z takim błędem to potem jak robię try catch to albo się wykona albo gdzie tablica nie jest argumentem dostaje zadeklarowana treść błędu
const oryginalna = [1, 2, 3];
const referencja = oryginalna; // referencja do tej samej tablicy
const osobnaKopia = [...oryginalna]; // nowa tablica - kopia
referencja.push(4);
console.log("Oryginalna:", oryginalna);
console.log("Referencja:", referencja);
console.log("Osobna kopia:", osobnaKopia);
// wskazują na tę samą tablicę w pamięci
console.log(referencja[0]);
for (let i = 0; i < referencja.length; i++) {
console.log("Element", i, ":", referencja[i]);
}
console.log(typeof(referencja));
console.log(Array.isArray(referencja)); // true
const arrayLike = {
0: "pierwszy",
1: "drugi",
2: "trzeci",
length: 3
};
console.log("Array-like:", arrayLike);
const prawdziwaTablica = Array.from(arrayLike);
console.log("Prawdziwa tablica:", prawdziwaTablica);
function sumuj(tablica) {
// Walidacja
if (!Array.isArray(tablica)) {
throw new TypeError("Argument musi być tablicą");
}
return tablica.reduce((sum, num) => sum + num, 0);
}
console.log("Suma [1,2,3]:", sumuj([1, 2, 3]));
try {
sumuj("nie tablica");
} catch (e) {
console.log("Błąd:", e.message);
}
function bezpiecznePrzetwarzanie(dane) {
if (Array.isArray(dane)) {
console.log("Przetwarzam tablicę:", dane.length, "elementów");
return dane.map(x => x * 2);
} else if (typeof dane === "object" && dane !== null) {
console.log("Przetwarzam obiekt");
return Object.values(dane);
} else {
console.log("Nieznany typ");
return [];
}
}
bezpiecznePrzetwarzanie([1, 2, 3]);
bezpiecznePrzetwarzanie({ a: 1, b: 2 });
bezpiecznePrzetwarzanie("tekst");
Sparse Arrays
Jest możliwe, że tworzymy tablice która nie ma jednorodnego indeksu, będzie miała dziury, bo będą indeksy bez przypisanych wartości.
Możemy tworzyć sparse arrays na kilka sposób w JS.
Metody 0pomijaja puste sloty, jak nie ma undefined inaczej się to zachowuje.
Jeśli stworzymy tablice która jest pusta, a po drodze dopiszemy wartość, to widać dwa indeksy przypisane, natomiast puste są puste nie widać ich jako wartości, tylko jako empty.
Metody tablicowe nie zachowują się tak samo przy tablicy z dziurami:
For each, map filter pomijają puste sloty.
For of pętla for of iteruje się przez wszystkie indeksy ale te co maja pusta wartość zwracają undefined.
Jak się wykonuje na tych elementach obliczenia, operacje, bez sprawdzenia czy tam jest wartość może to się zepsuć.
const sparseArray = new Array(10); // Tworzy tablicę o długości 10 z pustymi miejscami
console.log("Pusta tablica:", sparseArray);
sparseArray[2] = "A";
sparseArray[5] = "B";
console.log("Tablica po dodaniu elementów:", sparseArray);
// forEach pomija holes
console.log("forEach:");
sparseArray.forEach((val, i) => {
console.log("Indeks", i, ":", val);
});
// Nie wypisze indeksów 1 i 3
for (let i = 0; i < sparseArray.length; i++) {
console.log(i, ":", sparseArray[i]);
}
// Wypisze undefined dla holesMetody tablicowe
forEach - wykonuje funkcje dla każdego elementu tablicy, alternatywa dla pętli for, jest bardziej czytelna, nie zwraca wartości, służy do wykonywania "efektów ubocznych" wyświetlanie, modyfikacja elementów. Jeśli potrzebujemy wartości z tablicy musimy zrobić to w ciele funkcji, bo inaczej zwróci undefined
map - transformacja elementów, działa w ten sposób, że bierze naszą tablicę i na każdym elemencie tej tablicy wykonuje transformacje którą zadaliśmy, wykonuje te mapowanie na KOPI tablicy tj tworzy nową i dopiero w tej nowej tworzy transformacje, to oznacza ze map zwraca tablice z długością ta co oryginalna, ale ze zmienionymi wartościami, potrzebujemy funkcji która mówi jakie te elementy będą przetwarzane, mimo przypisania w funkcji to tworzy i tak kopię.
filter - filtrujemy elementy, tablica wyjściowa jest nowa tablica, ale tych elementów może mieć inna ilość bo np. możemy te elementy pominąć
// 1. FOREACH - iteracja z efektami ubocznymi
const liczby = [1, 2, 3, 4, 5];
console.log("forEach:");
liczby.forEach(liczba => {
console.log(liczba * 2);
});
// ForEach z indeksem
const owoce = ["jabłko", "banan", "pomarańcza"];
owoce.forEach((owoc, indeks) => {
console.log(indeks + ": " + owoc);
});
// ForEach NIE zwraca wartości
const wynik1 = owoce.forEach(owoc => owoc.toUpperCase());
console.log("forEach zwraca:", wynik1); // undefined
console.log("MAP - transformacja elementów");
// 2. MAP - transformacja elementów
const liczby2 = [1, 2, 3, 4, 5];
const podwojone = liczby2.map(n => n * 2);
console.log("Podwojone:", podwojone);
// Map zwraca nową tablicę
console.log("Oryginalna:", liczby2); // Nie zmieniona
console.log("Nowa:", podwojone);
// Map z obiektami
const uzytkownicy = [
{ imie: "Jan", wiek: 30 },
{ imie: "Anna", wiek: 25 },
{ imie: "Piotr", wiek: 35 }
];
const imiona = uzytkownicy.map(user => user.imie);
console.log("Imiona:", imiona);
// Map z indeksem
const numerowane = owoce.map((owoc, i) => (i + 1) + ". " + owoc);
console.log("Numerowane:", numerowane);
// 3. FILTER - filtrowanie elementów
console.log("FILTER - filtrowanie elementów");
const liczby3 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const parzyste = liczby3.filter(n => n % 2 === 0);
console.log("Parzyste:", parzyste);
const wieksze = liczby3.filter(n => n > 5);
console.log("Większe niż 5:", wieksze);
// Filter z obiektami
const aktywni = uzytkownicy.filter(user => user.wiek >= 30);
console.log("Aktywni (>=30):", aktywni);
// 4. ŁĄCZENIE metod
console.log("Łączenie metod:");
const wynik2 = liczby3
.filter(n => n % 2 === 0) // Tylko parzyste
.map(n => n * n) // Podnieś do kwadratu
.filter(n => n > 20); // Większe niż 20
console.log("Łączenie:", wynik2);
Poniżej z obiektu tresciStrony filtrujemy by wywaliło nam same tresci z about np.
const tresciStrony = [
{ id: 1,
section: "header",
title: "Strona Główna",
content: "Witamy na naszej stronie!",
author: "Jan Kowalski",
creationDate: "2024-01-15",
isActive: true
},
{ id: 2,
section: "about",
title: "O Nas",
content: "Jesteśmy firmą zajmującą się...",
author: "Anna Nowak",
creationDate: "2024-02-20",
isActive: true
}
];
console.log(tresciStrony);
const wynik = tresciStrony.filter(item => item.section === "about");
console.log(wynik);
function pokazSekcje(){
}
console.log(pokazSekcje(tablica, sekcja)) // about | header
function pokazSekcje(tablica, sekcja) {
return tablica.filter(item => item.section === sekcja);
}
console.log(pokazSekcje(tresciStrony, "about")); // about | headera tu jest filtrowanie po dowolnej sekcji - wsadzenie funkcji w parametry UNIWERSALNOŚĆ
function pokazSekcje(tablica, sekcja) {
return tablica.filter(item => item.section === sekcja);
}
console.log(pokazSekcje(tresciStrony, "about"));
Przeszukiwanie tablicy
find - przerywa iteracje po znalezieniu pierwszego dopasowania, nie sprawdza dalszych elementów, a jak żaden z warunków nie jest spełniony to zwraca undefined. Jeśli chcemy pokazać wszystkie elementy spełniające daną zależność to find się do tego nie nadaje, zamiast find użyć filter który zwróci wszystkie elementy
findindex - zwraca indeks a nie wartość, ale działa tak samo j.w. a jak nic nie znaleziono zwraca -1, przydatne przy pozycji elementu a nie wartości.
some - sprawdza czy przynajmniej 1 element spełnia warunek, zwraca wartosc boolean true false, normalnie trzeba warunek w if opakować a tutaj odrazu some mowi jak znajdzie pierwsze true to juz zwraca true. logiczny or
every - sprawdza czy wszystkie elementy danej tablicy spełniają warunek, zwraca boolean, przerywa po napotykaniu pierwszego false, jeśli chociaż 1 element tablicy nie spelnia warunku to przerywa. logiczny and
Używają short circut, dość wydajne i jeśli to mają być proste testy to one sa lepsze niż filter i map, jeśli nie potrzeba listy wartości.
Używać tego przy walidacji formularza: mamy obiekty np. pola formularza, możemy wyciągnąć i opisać elementy z html.
Można te metody chainować.
Możemy robić dwa filtrowania.
// 1. FIND - znajdź pierwszy element
const liczby = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const pierwszaParzysta = liczby.find(n => n % 2 === 0);
console.log("Pierwsza parzysta:", pierwszaParzysta); // 2
const pierwszaWieksza = liczby.find(n => n > 5);
console.log("Pierwsza > 5:", pierwszaWieksza); // 6
// Find gdy nic nie znaleziono
const nieznaleziona = liczby.find(n => n > 100);
console.log("Nie znaleziono:", nieznaleziona); // undefined
// 2. FIND z obiektami
const uzytkownicy = [
{ id: 1, imie: "Jan", wiek: 30 },
{ id: 2, imie: "Anna", wiek: 25 },
{ id: 3, imie: "Piotr", wiek: 35 }
];
const uzytkownik = uzytkownicy.find(u => u.id === 2);
console.log("Znaleziony użytkownik:", uzytkownik);
const starszy = uzytkownicy.find(u => u.wiek > 30);
console.log("Starszy niż 30:", starszy);
// 3. FINDINDEX - znajdź indeks
const indeksParzysta = liczby.findIndex(n => n % 2 === 0);
console.log("Indeks pierwszej parzystej:", indeksParzysta); // 1
const indeksUzytkownika = uzytkownicy.findIndex(u => u.imie === "Anna");
console.log("Indeks Anny:", indeksUzytkownika); // 1
// FindIndex gdy nic nie znaleziono
const indeksNieznaleziony = liczby.findIndex(n => n > 100);
console.log("Indeks nieznaleziony:", indeksNieznaleziony); // -1
// 4. SOME - czy jakikolwiek spełnia warunek
const maParzyste = liczby.some(n => n % 2 === 0);
console.log("Czy są parzyste?", maParzyste); // true
const maWieksze100 = liczby.some(n => n > 100);
console.log("Czy są > 100?", maWieksze100); // false
// Some z obiektami
const maAktywnych = uzytkownicy.some(u => u.wiek >= 30);
console.log("Czy są użytkownicy >= 30?", maAktywnych); // true
// 5. EVERY - czy wszystkie spełniają warunek
const wszystkieDodatnie = liczby.every(n => n > 0);
console.log("Czy wszystkie > 0?", wszystkieDodatnie); // true
const wszystkieParzyste = liczby.every(n => n % 2 === 0);
console.log("Czy wszystkie parzyste?", wszystkieParzyste); // false
// Every z obiektami
const wszystkiePelnioletni = uzytkownicy.every(u => u.wiek >= 18);
console.log("Czy wszyscy pełnoletni?", wszystkiePelnioletni); // true
// Walidacja formularza
const pola = [
{ nazwa: "email", wartość: "[email protected]", wypelnione: true },
{ nazwa: "haslo", wartość: "12345678", wypelnione: true },
{ nazwa: "imie", wartość: "", wypelnione: false }
];
const wszystkieWypelnione = pola.every(p => p.wypelnione);
console.log("Formularz kompletny?", wszystkieWypelnione); // false
const ktorePuste = pola.find(p => !p.wypelnione);
console.log("Pierwsze puste pole:", ktorePuste.nazwa);
//Kombinacja metod
const produkty = [
{ nazwa: "Laptop", cena: 3000, dostepny: true },
{ nazwa: "Mysz", cena: 50, dostepny: true },
{ nazwa: "Monitor", cena: 1500, dostepny: false }
];
// Czy wszystkie dostępne produkty są tańsze niż 4000?
const wszystkieDostepneTanie = produkty
.filter(p => p.dostepny)
.every(p => p.cena < 4000);
console.log("Wszystkie dostępne tanie?", wszystkieDostepneTanie);
// Znajdź pierwszy drogi i dostępny
const drogiDostepny = produkty.find(p =>
p.dostepny && p.cena > 1000
);
console.log("Drogi dostępny:", drogiDostepny.nazwa);
Nie elastyczne, nie mogą używać złożonych warunków, ale są szybsze, prostsze, sprawdzają równość waurnku:
includes - sprawdza obecność i zwraca true false, szybki tekst
includes z NaN jeśli jest NaN to jest true a jeśli używamy indeks of to nie działa, lepsza alternatywa dla index of
// 1. INCLUDES - sprawdź obecność
const owoce = ["jabłko", "banan", "pomarańcza", "gruszka"];
const maBanana = owoce.includes("banan");
console.log("Ma banana?", maBanana); // true
const maArbuz = owoce.includes("arbuz");
console.log("Ma arbuza?", maArbuz); // false
// Includes z liczbami
const liczby = [1, 2, 3, 4, 5];
console.log("Ma 3?", liczby.includes(3)); // true
console.log("Ma 10?", liczby.includes(10)); // false
// 2. INCLUDES z NaN - specjalne zachowanie
const zNaN = [1, 2, NaN, 4];
console.log("Ma NaN (includes)?", zNaN.includes(NaN)); // true
console.log("Ma NaN (indexOf)?", zNaN.indexOf(NaN)); // -1 (nie działa!)
// 3. INCLUDES z punktu startowego
const lista = ["a", "b", "c", "d", "e"];
console.log("Ma 'c'?", lista.includes("c")); // true
console.log("Ma 'c' od indeksu 3?", lista.includes("c", 3)); // false
// 4. INDEXOF - znajdź indeks
const indeksBanan = owoce.indexOf("banan");
console.log("Indeks banana:", indeksBanan); // 1
const indeksArbuz = owoce.indexOf("arbuz");
console.log("Indeks arbuza:", indeksArbuz); // -1
// IndexOf z punktu startowego
const powtorzone = ["a", "b", "c", "a", "b", "c"];
console.log("Pierwsze 'a':", powtorzone.indexOf("a")); // 0
console.log("'a' od indeksu 1:", powtorzone.indexOf("a", 1)); // 3
// 5. LASTINDEXOF - szukaj od końca
console.log("Ostatnie 'a':", powtorzone.lastIndexOf("a")); // 3
console.log("Ostatnie 'b':", powtorzone.lastIndexOf("b")); // 4
// LastIndexOf z punktu końcowego
console.log("'a' przed indeksem 2:", powtorzone.lastIndexOf("a", 2)); // 0
// 6. STARY vs NOWY sposób sprawdzania
// Stary sposób (przed ES2016)
const czyMa1 = owoce.indexOf("banan") !== -1;
console.log("Stary sposób:", czyMa1);
// Nowy sposób (ES2016+)
const czyMa2 = owoce.includes("banan");
console.log("Nowy sposób:", czyMa2);
// Nowy jest czytelniejszy!
// 7. PROBLEM z obiektami
const obiekty = [
{ id: 1, nazwa: "A" },
{ id: 2, nazwa: "B" }
];
// To NIE DZIAŁA - porównuje referencje
console.log("Ma obiekt?", obiekty.includes({ id: 1, nazwa: "A" })); // false
// Trzeba użyć find/some
const maObiekt = obiekty.some(obj => obj.id === 1);
console.log("Ma obiekt (some)?", maObiekt); // trueObiekty
Różnica między obiektem a tablicą - używamy swoich kluczy, nie indeksów
literał obiektu - powołujemy zmienną, dajemy {} dajemy klucz i wartość tego klucza, wartość może być prymitywną ale i złożoną, to oznacza ze możemy jako jedną z właściwości obiektu mieć tablice lub inny obiekt, pozwala to na tworzenie skomplikowanych struktur, możemy przechowywać funkcje - metody obiektu, THIS. metoda do obsługi tego obiektu.
const osoba = {
imie: "Jan",
nazwisko: "Kowalski",
wiek: 30,
};
console.log("Osoba:", osoba);
const uzytkownik = {
id: 1, // liczba
imie: "Anna", // string
aktywny: true, // boolean
adres: { // zagnieżdżony obiekt
ulica: "Główna 1",
miasto: "Warszawa"
},
hobby: ["czytanie", "sport"], // tablica
przedstawSie: function() { // metoda
return "Cześć, jestem " + this.imie;
}
};
console.log("Użytkownik:", uzytkownik);
console.log("Przedstawienie:", uzytkownik.przedstawSie());
Property shorthand - skrót zapisu jeśli mam zewnętrzne zmienne, możemy pominąć przypisanie wartości,
// 3. PROPERTY SHORTHAND (ES6)
const imie = "Piotr";
const wiek = 25;
const miasto = "Kraków";
// Stara składnia
const osoba1 = {
imie: imie,
wiek: wiek,
miasto: miasto
};
// Nowa składnia - shorthand
const osoba2 = {
imie,
wiek,
miasto
};
console.log("Shorthand:", osoba2);odnosi się to też do metod
// method shorthand
const kalkulator = {
// Stara składnia
dodaj: function(a, b) {
return a + b;
},
// Nowa składnia - shorthand
odejmij(a, b) {
return a - b;
},
pomnoz(a, b) {
return a * b;
}
};
console.log("Dodaj:", kalkulator.dodaj(5, 3));
console.log("Odejmij:", kalkulator.odejmij(5, 3));Jesteśmy w stanie dodawać nie tylko wartości do kluczy, ale i nazwy. Tworzymy zmienne, następnie chcemy obiekt gdzie jedna ze zmiennych to nazwa klucz a drugie to wartość, wtedy mogę włożyć to co chce mieć kluczem [wsadzam w te nawiasy] : wartość. Jeśli tworzymy obiekt nie jako literały, gdzie wszystkie nazwy są przypisane, jeśli potrzebujemy tworzyć klucz gdzie nazw jeszcze nie znamy to to jest właśnie metoda na to.
// 5. COMPUTED PROPERTY NAMES
const klucz = "email";
const wartosc = "[email protected]";
const obiekt1 = {
[klucz]: wartosc
}
console.log("Computed:", obiekt1); // { email: "[email protected]" }
// Bardziej złożone wyrażenia
const prefix = "user";
const id = 123;
const obiekt2 = {
[prefix + "_" + id]: "Jan Kowalski",
[`${prefix}_email`]: "[email protected]"
};
console.log("Computed złożone:", obiekt2);
console.log(prefix + "_" + id);
console.log(`${prefix}_${id}`);
const multiLineString=`
jeden ${prefix}_${id}
dwa
trzy ${prefix}_${id}`;
console.log(multiLineString);template literal - multi line string `
Referencja w obiektach, jesli stworze potem przypisze do nowej zmiennej to obie zmienne to oryginalna i referencja wskazuja na ten sam obiekt. To jak kopiowanie tablic ... const kopa = {...oryginal} można używać const bo dalej możemy zmieniać elementy itd. ale nie zmienimy przez pomyłkę typu.
sięganie do wartości obiektu obiekt.klucz
sięganie do zagnieżdżonych obiektów obiekt.klucz.glebiej
Tworzenie obiektów dynamicznie: kawałek template np. funkcja tworząca użytkownika, zwraca obiekt
// Dynamiczne tworzenie obiektów
function utworzUzytkownika(imie, email) {
return {
imie,
email,
utworzono: new Date(),
aktywny: true,
wyswietl() {
console.log(`${this.imie} (${this.email})`);
}
};
}
const nowyUzytkownik = utworzUzytkownika("Piotr", "[email protected]");
console.log("Nowy użytkownik:", nowyUzytkownik);
Dot notation
// 1. DOT NOTATION - podstawowe użycie
const osoba = {
imie: "Jan",
wiek: 30,
miasto: "Warszawa"
};
console.log("Imię (dot):", osoba.imie);
console.log("Wiek (dot):", osoba.wiek);
console.log("Imię (bracket):", osoba["imie"]);
console.log("Wiek (bracket):", osoba["wiek"]);
osoba.telefon="12345";
obiekt.keys kiedy coś modyfikujemy nie wiemy jakie są wszystkie klucze itd chcemy sprawdzić ten obiekt jego nazwy kluczy by potem sie na nich przeiterowac
const kluczeObiektu=Object.keys(osoba);
for (i=0; i<kluczeObiektu.length; i++){
console.log(kluczeObiektu[i]," " ,osoba[kluczeObiektu[i]]);
}