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.

  1. błędy które wynikają z nieprawidłowych danych podanych przez user
  2. operacja się nie wykonała bo np. nie było zasobów json czy plik który parsujemy ma nieprawidłowy format
  3. 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

  1. 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
  2. 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
  3. this stritct mode przełącznik mówi w takim trybie ma działać funkcja, nie da się wyciągać this z kontekstu globalnego undefined
  4. 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
  5. callback przekazanie funkcji do funkcji globalnie pokazuje window ale jako przekazaliśmy cos do funkcji to pokazuje nam ta funkcje
  6. 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
  7. arrow function

This zależy od wywoływania a nie definicji

  1. jak nie przekazujemy żadnej wartości = window, kontekst globalny
  2. przypisany jako metoda opakowany w obiekt dostaniemy ten obiekt/wskazuje obiekt
  3. 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

  1. 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
  2. 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
  3. 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
  4. ... utrata kontekstu
  5. bind przeglądarko nie trać kontekstu to nadal ma być ten obiekt
  6. Method Chaining Można wykorzystać więcej niż 1 metodę w obiekcie np. kalkulator, tworzymy łańcuch z metod które obiekt ma (kalkulator)
  7. 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ść.

  1. 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
  2. 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
  3. 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ń

  1. 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
  2. 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

  1. 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
  2. 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
  3. 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ć
  4. 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 holes

Metody 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 | header

a 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); // true

Obiekty

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]]);
}