JS cz.3
Podstawowe optymalizacje pętli:
-wyciągnąć element/wartość przed for (np. tablica.lenght) – żeby nie procesować tej danej
-specjalne typy pętli/ specjalne konstrukcje
dla przypomnienia:
PĘTLA FOR — pętla „po liczbach / krokach”
Co iteruje?
licznik, czyli liczby: 0, 1, 2, 3…
Po co?
Gdy potrzebujesz kontroli:
- zacząć od konkretnej liczby
- skakać co 1, co 2, co 5
- zatrzymać się w dowolnym momencie
- wykonać pętlę np. 100 razy
for (let i = 0; i < 5; i++) {
console.log("i =", i);
}const owoce = ["jabłko", "banan", "gruszka"];
for (let i = 0; i < owoce.length; i++) {
console.log(i, owoce[i]);
}CZYLI: „Dla zmiennej 'i', zaczynając od 0, dopóki i jest mniejsze niż 5, w każdej iteracji(w każdym kolejnym kroku) zwiększaj 'i' o 1 i wykonaj blok kodu{}.”
FOR...OF — pętla „po wartościach tablicy”
Co iteruje?
➡wartości tablicy, np.:
"jabłko", "banan", "gruszka"
Po co?
➡Gdy chcesz po prostu przejść przez elementy tablicy i nic więcej.
➡ Bez indeksów, bez kombinacji.
Pętla for...of w JavaScript to specjalna odmiana pętli do iterowania po wartościach tablic, stringów i innych obiektów iterowalnych (np. Map, Set).
for...of iteruje po wartościach tablicy według indeksów od 0 do length-1. (brakujące indeksy = undefined, właściwości kluczy obiektów będą pomijane)
- Przechodzi po każdej wartości kolekcji
- Nie wymaga licznika ani indeksu (choć indeks można uzyskać przez
.entries()) - Czytelniejsza niż klasyczny
forw przypadku prostego iterowania po tablicy
for (const element of kolekcja) {
// kod do wykonania dla każdego elementu
}element – zmienna, która przy każdej iteracji przyjmuje wartość kolejnego elementu tablicy/iterowalnego obiektu
const fruits = ["jabłko", "banan", "gruszka"];
for (const fruit of fruits) {
console.log(fruit);
}
// wypisze jabłko, banan, gruszkaCZYLI: „Dla każdego owocu w tablicy owoców, pokaż w konsoli jego nazwę.”
inaczej: Dla każdej zmiennej x/elementu tablicy zmiennej y zrób to: blok kodu
Co się dzieje:
const fruits = ["jabłko", "banan", "gruszka"];- Tworzymy tablicę
fruitszawierającą trzy elementy:"jabłko","banan"i"gruszka"
- Tworzymy tablicę
for (const fruit of fruits)- Pętla mówi: „weź każdy element z tablicy
fruitskolejno i przypisz go do zmiennejfruit” const fruit= zmienna tymczasowa, która w każdej iteracji przechowuje aktualny owoc
- Pętla mówi: „weź każdy element z tablicy
console.log(fruit);- Wypisuje w konsoli wartość zmiennej
fruit - W pierwszej iteracji:
"jabłko" - W drugiej iteracji:
"banan" - W trzeciej iteracji:
"gruszka"
- Wypisuje w konsoli wartość zmiennej
- Po ostatnim elemencie pętla kończy działanie
for...of po STRINGU
for...of przechodzi po każdej literze w stringu.
const fruits = ["jabłko", "banan"];
for (const fruit of fruits) {
console.log("Owoc:", fruit);
for (const letter of fruit) {
console.log(" - litera:", letter);
}
}Pętla for...of iteruje po tym, co jest po prawej stronie of.
Jeśli tam jest tablica → dostajesz elementy tablicy.
Jeśli tam jest string → dostajesz litery string
for (const fruit of fruits)
Ta linijka tworzy zmienną fruit - deklaruje ją, w tym przypadku mamy 3 zmienne.fruit nie jest częścią tablicy fruits. Sami tworzymy zmienną fruit w pętli. A for...of po prostu wstawia do niej kolejne elementy tablicy.
PĘTLA FOR IN — pętla „po kluczach (nazwach pól)”
Co iteruje?
➡klucze obiektu
(bo tablica też jest obiektem, tylko kluczami są indeksy - które zawsze są STRINGAMI, choć JS może automatycznie konwertować je na liczby(?))
Dla tablicy: "0", "1", "2", …"
Dla obiektu:
{ imie: "Ala", wiek: 25 }klucze = "imie", "wiek"
Po co?
➡Do iterowania po obiektach, nie po tablicach.
➡Gdy chcesz poznać nazwy pól i ich wartości.
to iteracja po indexach - iteruje po KLUCZACH nie po wartościach (kluczem może być numer indexu, któremu przyporządkowana jest wartość lub string, któremu przyporządkowana jest wartość np. imię:Anna)
console.log("For...in (indeksy jako stringi):");
for (const indeks in owoce) {
console.log("Indeks:", indeks, "Typ:", typeof indeks);
console.log("Wartość:", owoce[indeks]);
}
CZYLI: „Dla każdego klucza w obiekcie/ tablicy wykonaj kod znajdujący się w pętli.”
| Pętla / Metoda | Co iteruje? | Co wypisuje w console.log? |
Uwagi / typowe zastosowanie |
|---|---|---|---|
Klasyczny for |
Liczbę od 0 do length-1 |
Wartości tablicy: arr[i] |
Wymaga ręcznej kontroli indeksu. Można użyć i do obliczeń. |
for...of |
Wartości tablicy | Elementy tablicy bezpośrednio | Najprostszy sposób iterowania po wartościach. Nie iteruje po dodatkowych właściwościach/tablicy. |
for...in |
Klucze obiektu (indeksy w tablicy) | Klucze tablicy jako stringi ("0", "1"…) |
Działa po wszystkich kluczach obiektu/tablicy, w tym dodatkowych własnościach. Często nie zaleca się dla tablic, bo może iterować po metodach prototypu. |
| Pętla | Iteruje po | Idealna do | Przykład |
|---|---|---|---|
| for | liczby (indeksy) | precyzyjna kontrola | liczenie od 0 do n |
| for...of | wartości tablicy | przeglądanie elementów | owoce, lista zakupów |
| for...in | klucze obiektu | obiekty (pola) | osoba.imie, osoba.wiek |
JS_7.3 - informacje dotyczące optymalizacji - do przeanalizowania
ITERATOR -to obiekt, który pozwala „czytać” elementy kolekcji po jednym.
Iterować (od łac. iterare – „powtarzać”) znaczy powtarzać jakąś czynność dla kolejnych elementów (np. tablicy, zbioru Set, mapy Map, łańcucha znaków itp.). — czyli przechodzić po każdym elemencie po kolei i coś z nim robić
Iterator „pamięta”, na którym elemencie aktualnie się znajduje i ma metodę next(), która zwraca kolejny element - coś jak „czytnik”, który przesuwa się dalej i czyta kolejne elementy, pytając czy to już ostatni?
Każda tablica ma wbudowany iterator.
Teraz możemy go „ręcznie” przesuwać:
Każde wywołanie .next() daje następny element aż do końca.
Iterator może być ukryty np. pod przyciskiem w HTMLu
Duży skrót: do czego służy next()?
do ręcznego iterowania po tablicy lub stringu
do budowania własnych pętli
do głębokiego zrozumienia mechanizmu pętli for...of
(bo for of działa wewnętrznie dokładnie tak jak next(), tylko automatycznie)
const fruits = ["jabłko", "banan"];
const iterator = fruits[Symbol.iterator]();
console.log(iterator.next()); // { value: "jabłko", done: false }
console.log(iterator.next()); // { value: "banan", done: false }
console.log(iterator.next()); // { value: undefined, done: true }Za KAŻDYM razem, gdy wywołasz next(), dostajesz obiekt:
{ value: ..., done: ... }
- value → kolejny element
- done → informacja czy iteracja się skończyła
false→ nadal są elementytrue→ koniec iteracji
Najpierw trzeba sprawdzić czy można używać tej metody na danym typie danych
- czy obiekt jest iterable
function czyIterable(obj) {
return obj != null && typeof obj[Symbol.iterator] === "function";
}
console.log("[] jest iterable:", czyIterable([])); // true
console.log("{} jest iterable:", czyIterable({})); // false
console.log("'tekst' jest iterable:", czyIterable("tekst")); // true
w JavaScript mamy różne sposoby definiowania funkcji, a od tego zależy m.in. hoisting i sposób użycia zmiennej
- Function Declaration (deklaracja funkcji)
Funkcje deklaruje się, a potem wywołuje, jest ona hoistowana, więc można ją najpierw wywołać a potem zadeklarować
sayHi(); // działa!
function sayHi() { console.log("Hi");- Function Expression (wyrażenie funkcyjne)
const greet = function() {
console.log("Cześć!");
};Tutaj funkcja jest przypisana do zmiennej. Nie ma hoistingu jak deklaracja — nie możesz wywołać greet() przed tą linią, bo zmienna jeszcze nie istnieje.
- Arrow Function (funkcja strzałkowa)
const greet = () => {
console.log("Cześć!");
};
To też function expression, więc nie jest hoistowana. Nie ma własnego this ani arguments — zachowuje się inaczej niż zwykła funkcja
|
Pojęcie |
Co to jest |
Gdzie występuje |
|
Parametr |
Nazwa zmiennej, którą deklarujesz w definicji
funkcji, scope blokowy |
W nagłówku funkcji |
|
Argument |
Wartość, którą przekazujesz do funkcji przy jej wywołaniu |
W momencie wywołania |
function greet(name) {
console.log("Cześć, " + name);
}
// wywołanie funkcji z argumentem "Ala"
greet("Ala"); Do funkcji możemy podac dowolne argumenty – to JS obsługuje jako pseudo tablica – to obiekt jak tablica, ale nie ma on metod tablicowych (nie obsługuje narzędzi typowych dla tablic). Jeżeli chcemy otrzymać listę argumentów przekazanych do funkcji to możemy to zrobić w ten sposób:
// Obiekt arguments
function sumujWszystkie() {
console.log("Arguments:", arguments);
console.log("Typ:", typeof arguments);
console.log("Length:", arguments.length);
const argumentsArray = Array.from(arguments); // konwersja na tablicę
console.log("Po konwersji:", Array.isArray(argumentsArray)); // true
console.log("Tablica:", argumentsArray);
let suma = 0;
for (let i = 0; i < arguments.length; i++) {
suma += arguments[i];
}
return suma;
}
const suma2 = sumujWszystkie(1, 2, 3, 4, 5, 6);
console.log("Suma wszystkich:", suma2); // 21Jeżeli chcemy sprawdzić czy zmienna jest tablicą:
const fruits = ["jabłko", "banan"];
const notArray = { a: 1 };
console.log(Array.isArray(fruits)); // true
console.log(Array.isArray(notArray)); // falseZmienna blokowa – wewnątrz funkcji, zmienna globalna – poza funkcją
Scope chaining – JS sprawdza czy zmienna nie została zadklarowana, szuka wyżej - to co jest poza pętlą, poza blokiem nie będzie działało
Kluczowe punkty shadowingu:
- Dotyczy nazw zmiennych (var, let, const) oraz parametrów funkcji.
- Zmienne w wewnętrznym bloku mają pierwszeństwo przed zmiennymi o tej samej nazwie w zewnętrznym bloku.
Może prowadzić do błędów i trudnych do znalezienia bugów, jeśli nie jesteśmy świadomi, że nazwy się powtarzają
CZYLI - Globalna x istnieje cały czas i ma wartość 10. Jeśli w funkcji tworzymy lokalną x (shadowing) to ta lokalna x „przykrywa” globalną tylko wewnątrz funkcji. Wszystkie odwołania do x w funkcji używają lokalnej zmiennej, nie globalnej. Po wyjściu z funkcji: Lokalna x znika, a globalna x pozostaje nietknięta.
NAZYWANIE FUNKCJI
-CamelCase jest też stosowany do funkcji
-funkcja powinna komunikować co robi
- funkcje zwykle wykonują akcje wiec ich nazwy powinny zawierać czasownik
Is, can, has – początek, długość nazwy proporcjonalna do zakresu – lokalne krótsze, opisowe to te używane w wielu miejscach
ARROW FUNCTION
Funkcja strzałkowa (arrow function) to krótszy zapis funkcji w JavaScript, wprowadzony w ES6. Ma kilka cech, które odróżniają ją od klasycznej funkcji (function).
const greet = () => {
console.log("Cześć!");
}
greet(); // "Cześć!"- Strzałka
=>zastępuje słowofunction. - Funkcję przypisuje się do zmiennej =
- Jeśli ciało funkcji ma tylko jedno wyrażenie, można pominąć
{}ireturn.
const greet = () => console.log("Cześć!");Funkcje arrow są anonimowe, można je przypisać do zmiennej, nie mają własnej nazwy co ma znaczenie przy debugowaniu kodu i używaniu rekurencji. Rekurencja w programowaniu to technika, w której funkcja wywołuje samą siebie, żeby rozwiązać problem.
Arrow Functions mogą być wieloliniowe albo być w ciele funkcji, możemy je łączyć z innymi funkcjami
Różnice między arrow function a zwykłą funkcją:
| Cecha | Zwykła funkcja | Arrow function |
|---|---|---|
this | dynamiczne | dziedziczone z otaczającego kontekstu |
arguments | dostępne | brak (można użyć rest ...args) |
| Hoisting | tak | nie (jak function expression) |
| Krótsza składnia | nie | tak |
Default parameters (parametry domyślne)
Pozwalają ustawić wartość domyślną dla parametru funkcji, jeśli argument nie zostanie przekazany lub jest undefined.
function greet(name = "Gość") {
console.log("Cześć, " + name);
}
greet("Ala"); // Cześć, Ala
greet(); // Cześć, GośćUWAGA: null nie aktywuje wartości domyślnej, bo JS traktuje null jako prawidłową wartość:
*ciekawostka
W JS prompt() może przyjmować drugi argument – to jest wartość domyślna, która pojawia się w polu input:
const name = prompt("Jak masz na imię?", "Ala");
console.log(name);- Jeśli użytkownik nie wpisze nic i kliknie OK, wartość będzie
""(pusty string). - Jeśli użytkownik kliknie Anuluj, wynik będzie
null.
Rest parameters (parametry reszty)
- Pozwalają zebrać dowolną liczbę argumentów w jedną tablicę.
- Zapisuje się je jako
...nazwa.
function showColors(...colors) {
console.log("Kolory, które podałeś:", colors);
}
showColors("czerwony", "zielony", "niebieski");
| Cecha | Default parameter | Rest parameter |
|---|
| Cel | ustawienie wartości, jeśli brak argumentu | zebranie wszystkich nadmiarowych argumentów w tablicę |
| Składnia | param = wartość | ...param |
| Liczba argumentów | pojedynczy parametr | dowolna liczba |
Higher-order function (funkcja wyższego rzędu)
W JavaScript funkcja wyższego rzędu to funkcja, która:
Przyjmuje inną funkcję jako argument, lub zwraca funkcję jako wynik.
Innymi słowy – funkcja, która operuje na innych funkcjach.
function greetUser(name, upperCaseName) {
console.log("Cześć, " + upperCaseName(name));
}
function toUpperCase(name) {
return name.toUpperCase();
}
greetUser("Ala", toUpperCase); // Cześć, ALAFirst-class citizens (obywatele pierwszej klasy)
- W JS funkcje są first-class citizens, czyli pełnoprawnymi obiektami.
- Co to oznacza w praktyce:
- Można je przypisać do zmiennej
- Można je przekazywać jako argumenty do innych funkcji
- Można je zwracać z funkcji
- Mogą mieć własne właściwości
Callback pattern – funkcje jako argumenty
- Funkcja przekazana do innej funkcji jako argument, która zostanie wywołana w określonym momencie.
- Pozwala programowi wywołać kod “po czymś”, np. po zakończeniu operacji asynchronicznej.
Zwracanie funkcji z funkcji – tworzenie closures (domknięć)
- Funkcja może zwracać inną funkcję.
- Funkcja wewnętrzna “pamięta” zmienne z otaczającej funkcji – to właśnie closure.
function greet(name) {
return function() {
console.log("Cześć, " + name + "!");
};
}
const greetAla = greet("Ala");
greetAla(); // Cześć, Ala!W tym przypadku przypisanie funkcji greetAla do nowej zmiennej pozwoli przywoływać ją wielokrotnie = bo funkcja zwracana przez funkcję to nowa funkcja. Każde wywołanie greet("Ala") tworzy nową funkcję, która “pamięta” name.