Tablice (arrays) służą do przechowywania uporządkowanych kolekcji danych. W JavaScript tablica jest obiektem specjalnego rodzaju, który przechowuje wartości w indeksowanych (ponumerowanych) pozycjach. Indeksy w tablicy zaczynają się od 0 (pierwszy element ma indeks 0, drugi indeks 1, itd.). Tablice w JavaScript mogą zawierać elementy różnych typów (np. liczby, stringi, a nawet inne tablice czy obiekty), choć często dla przejrzystości przechowuje się w jednej tablicy dane jednego rodzaju. W tej sekcji omówimy tworzenie tablic, dostęp do ich elementów, iterację oraz najważniejsze metody do operacji na tablicach. Pokażemy też, na czym polega destrukturyzacja tablic.
Tworzenie tablic i dostęp do elementów
Tablicę najłatwiej utworzyć za pomocą literału tablicy, czyli elementów ujętych w nawiasy kwadratowe []
. Przykład deklaracji tablicy i odczytu jej elementów:
const liczby = [10, 20, 30, 40]; // tablica liczb
console.log(liczby[0]); // 10 (pierwszy element, indeks 0)
console.log(liczby[3]); // 40 (czwarty element, indeks 3)
console.log(liczby.length); // 4 (właściwość length oznacza liczbę elementów tablicy)
W powyższym przykładzie liczby
to tablica zawierająca cztery liczby. Używając nazwaTablicy[indeks]
dostajemy się do konkretnego elementu. Należy pamiętać, że:
- Jeśli spróbujemy odczytać element pod nieistniejącym indeksem (np.
liczby[10]
w powyższej tablicy), otrzymamyundefined
, ponieważ taki element nie istnieje. - Właściwość
length
zawsze zwraca nominalną długość tablicy (ostatni indeks + 1). Można ją też ustawiać (przycięcie lub rozszerzenie tablicy), ale zazwyczaj odczytujemylength
, by iterować po tablicy.
Możliwe jest również utworzenie tablicy za pomocą konstruktora Array
(np. const arr = new Array(5)
tworzy tablicę o długości 5 z pustymi miejscami), ale literał []
jest bardziej powszechny i zalecany.
Elementy tablicy mogą być dowolnego typu, np.:
const rozne = [42, "tekst", true, null, {x:1, y:2}];
console.log(rozne[1]); // "tekst"
console.log(rozne[4].x); // 1 (dostęp do pola obiektu wewnątrz tablicy)
Powyższa tablica rozne
zawiera kolejno: liczbę, napis, wartość boolean, null oraz obiekt. Widać, że do obiektu wewnątrz tablicy można się dostać najpierw przez indeks tablicy, a następnie przez właściwość obiektu (rozne[4].x
).
Iteracja po tablicach
Bardzo często potrzebujemy przejść przez wszystkie elementy tablicy i coś z nimi zrobić (np. wypisać, przetworzyć). Istnieje kilka sposobów iterowania po tablicach:
const dane = [5, 10, 15];
// 1. Klasyczna pętla for z indeksem
for (let i = 0; i < dane.length; i++) {
console.log("Indeks", i, "wartość", dane[i]);
}
// 2. Pętla for...of (ES6) – iteruje bezpośrednio po wartościach
for (const element of dane) {
console.log("Wartość:", element);
}
// 3. Metoda forEach – wywołuje funkcję dla każdego elementu
dane.forEach((element, index) => {
console.log("Index:", index, "wartość:", element);
});
Wynik działania każdego z powyższych sposobów będzie podobny – w konsoli pojawią się wartości 5, 10, 15
(wraz z indeksami w przykładach 1 i 3). Omówienie sposobów:
- Pętla for z licznikiem – tradycyjny sposób znany z wielu języków. Dajemy pełną kontrolę nad indeksem, można iterację rozpoczynać/kończyć w dowolnym miejscu, przeskakiwać co kilka itp. Trzeba jednak uważać, by poprawnie zainicjować i aktualizować licznik oraz by warunek
i < dane.length
był prawidłowy (łatwo o błąd „out of range”). - Pętla for…of – nowszy, bardziej zwięzły sposób iteracji. Przy każdej iteracji zmienna (
element
w powyższym przykładzie) przyjmuje kolejną wartość z tablicy. Nie mamy bezpośrednio dostępu do indeksu (choć można go śledzić osobno lub użyć metodyentries()
), ale kod jest czytelniejszy, gdy indeks nie jest potrzebny. forEach
– metoda tablicowa przyjmująca funkcję zwrotną (callback). Dla każdego elementu tablicy wywoła tę funkcję, przekazując element, indeks i całą tablicę jako argumenty. W przykładzie użyliśmy funkcji strzałkowej, która wypisuje indeks i wartość.forEach
jest wygodny, gdy chcemy wykonać operację dla każdego elementu, nie interesuje nas przerwanie iteracji (nie można użyćbreak
wewnątrz forEach tak jak w pętli for) i nie potrzebujemy wyniku (forEach zawsze zwraca undefined).
Istnieje także pętla for…in, jednak w przypadku tablic nie jest zalecana – iteruje ona po właściwościach obiektu, a tablica to obiekt, którego „właściwościami” są indeksy i ewentualnie inne dodane właściwości. Może to dać nieoczekiwane wyniki lub złą kolejność dla tablic, dlatego do tablic preferujemy for, for…of lub metody wbudowane.
Popularne metody tablicowe
JavaScript dostarcza bogaty zestaw metod ułatwiających pracę z tablicami. Poniżej omówimy kilka często używanych metod:
push
– dodaje element na koniec tablicy. Zwraca nową długość tablicy.
Przykład:const arr = [1,2]; arr.push(3); // arr teraz [1,2,3]
.pop
– usuwa ostatni element tablicy i zwraca go.
Przykład:arr.pop(); // zwraca 3, arr staje się [1,2]
.unshift
– dodaje element na początek tablicy (przesuwając istniejące elementy w prawo). Zwraca nową długość.
Przykład:arr.unshift(0); // arr teraz [0,1,2]
.shift
– usuwa pierwszy element tablicy i zwraca go (przesuwa pozostałe w lewo).
Przykład:arr.shift(); // zwraca 0, arr staje się [1,2]
.indexOf
– wyszukuje podany element i zwraca jego indeks lub-1
jeśli nie znajdzie.
Przykład:[10,20,30].indexOf(20); // 1
.includes
– zwracatrue/false
czy dany element znajduje się w tablicy.
Przykład:[10,20,30].includes(25); // false
.slice
– zwraca nową tablicę będącą wycinkiem oryginalnej (nie zmienia oryginału). Przyjmuje indeks początkowy i końcowy (nie włącznie) wycinka.
Przykład:const a = [1,2,3,4]; const b = a.slice(1,3); // b = [2,3]
.splice
– uniwersalna metoda do modyfikowania tablicy: może usunąć, dodać lub zastąpić elementy w środku tablicy. Zwraca tablicę usuniętych elementów.
Przykład:const a = [1,2,3,4]; a.splice(1,2,9,8); // a = [1,9,8,4] (pod indeksem 1 usuwa 2 elementy i wstawia 9,8)
.forEach
– omówiony wyżej; wykonuje przekazaną funkcję dla każdego elementu (nie zwraca nowej tablicy).map
– tworzy nową tablicę, w której każdy element jest wynikiem wywołania przekazanej funkcji na odpowiadającym elemencie wejściowym.
Przykład:[1,2,3].map(x => x * 2); // wynik: [2,4,6]
(oryginalna tablica nie zmieniona).filter
– tworzy nową tablicę z tymi elementami, dla których przekazana funkcja zwróciła wartość prawdziwą (true).
Przykład:[1,2,3,4].filter(x => x % 2 === 0); // [2,4]
(wyfiltrowano liczby parzyste).reduce
– przetwarza tablicę do pojedynczej wartości poprzez wykonywanie funkcji akumulującej na kolejnych elementach. Funkcja ta otrzymuje akumulator i bieżący element, a zwraca nowy akumulator.reduce
przyjmuje także wartość początkową akumulatora.
Przykład (sumowanie):[1,2,3].reduce((acc, x) => acc + x, 0); // 6
.reduce
bywa trudniejszy do zrozumienia na początku, ale jest potężny – pozwala np. sumować, mnożyć, łączyć obiekty, a nawet realizować logikę każdej innej metody (map, filter) w jednym.find
– zwraca pierwszy element, dla którego funkcja testująca (callback) zwróci true. Jeśli nie znajdzie, zwracaundefined
.
Przykład:[{x:1},{x:2}].find(obj => obj.x===2); // {x:2}
.sort
– sortuje tablicę w miejscu (mutuje ją). Domyślnie sortuje elementy jak stringi Unicode, ale można przekazać własną funkcję porównującą do sortowania liczb lub obiektów według klucza.join
– łączy wszystkie elementy tablicy w jeden string, z podanym separatorem.
Przykład:[\"A\",\"B\",\"C\"].join(\"-\"); // \"A-B-C\"
.
To tylko kilka metod – jest ich więcej, ale te należą do najczęściej używanych. Ważne jest rozróżnienie, które metody mutują oryginalną tablicę (np. push, pop, shift, unshift, splice, sort) a które zwracają nową tablicę pozostawiając oryginał bez zmian (map, filter, slice, etc.).
Przykładowo, jeśli chcemy zachować oryginalne dane, lepiej użyć slice
niż splice
albo filter
zamiast usuwać elementy ręcznie. Natomiast gdy chcemy dokonać zmiany w miejscu (in-place), np. dodając element, użyjemy push
/unshift
lub usuwając pop
/shift
.
Destrukturyzacja tablic
Destrukturyzacja (destructuring) to wygodna składnia umożliwiająca wyciąganie wartości z tablic (lub obiektów) i przypisywanie ich do zmiennych, wszystko w jednej deklaracji. W przypadku tablic używamy do tego nawiasów kwadratowych po lewej stronie przypisania.
Przykład destrukturyzacji tablicy:
const coords = [100, 200, 300];
const [x, y, z] = coords;
console.log(x); // 100
console.log(y); // 200
console.log(z); // 300
Tutaj tablica coords
zawiera trzy liczby. Dzięki składni const [x, y, z] = coords;
trzy nowe zmienne x, y, z
otrzymują wartości kolejnych elementów tablicy coords
. Jest to równoważne trzem osobnym przypisaniom z użyciem indeksów, ale zapisane zwięźle w jednej linii.
Kilka przydatnych możliwości destrukturyzacji:
- Można zignorować niektóre elementy, wstawiając pusty przecinek. Np.
const [pierwszy, , trzeci] = [10, 20, 30]; // pomijamy drugi
Po tym przypisaniupierwszy == 10
,trzeci == 30
. Drugi element tablicy został pominięty. - Można przypisać tylko pierwsze kilka elementów do zmiennych, a resztę zgrupować w tablicę za pomocą operatora rest
...
.
Np.
const [head, ...tail] = [1, 2, 3, 4, 5];
console.log(head); // 1 console.log(tail); // [2, 3, 4, 5] Tutaj `head` to pierwszy element, a `tail` to tablica z pozostałymi.
Destrukturyzacja jest przydatna przy zamianie wartości dwóch zmiennych bez użycia zmiennej pomocniczej:
let a = 1, b = 2; [a, b] = [b, a];
console.log(a, b); // 2 1 Powyżej w jednej linii zamieniliśmy wartości zmiennych `a` i `b`.
Destrukturyzacja zwiększa czytelność, gdy wyciągamy wiele wartości z tablicy (np. wynik funkcji zwracającej tablicę) – zamiast pisać const val0 = arr[0]; const val1 = arr[1]; ...
, możemy to zrobić zwięźle.
Przykładowe operacje na tablicach
Aby zademonstrować praktyczną pracę z tablicami, rozważmy następujący przykład: Mamy tablicę liczb i chcemy z tej tablicy wybrać liczby parzyste, następnie podwoić te wartości i na końcu zsumować wszystkie otrzymane wyniki. Możemy to osiągnąć łańcuchowo za pomocą metod filter
, map
i reduce
:
const liczby = [1, 2, 3, 4, 5];
const parzyste = liczby.filter(num => num % 2 === 0); // wybieramy parzyste -> [2, 4]
const podwojone = parzyste.map(num => num * 2); // podwajamy każdy -> [4, 8]
const suma = podwojone.reduce((acc, num) => acc + num, 0); // sumujemy -> 12
console.log("Suma podwojonych parzystych:", suma);
Omówienie krok po kroku:
- Filter:
parzyste
zawiera wynikliczby.filter(...)
. Funkcja strzałkowanum => num % 2 === 0
pozostawia w tablicy tylko te elementy, dla których warunek „czy liczba jest parzysta” jest spełniony. Oryginalna tablicaliczby
pozostaje bez zmian. - Map: Na wynikowej tablicy parzystych liczb wywołujemy
map
, przekazując funkcję mnożącą każdy element przez 2. Otrzymujemy nową tablicępodwojone
z przekształconymi wartościami. - Reduce: Następnie redukujemy tablicę
podwojone
do pojedynczej wartości sumy. Funkcja akumulatora((acc, num) => acc + num)
dodaje kolejne liczby do akumulatoraacc
, zaczynając od zera (drugi argumentreduce
to 0 – wartość początkowa akumulatora). Wynik trafia do zmiennejsuma
.
Na końcu w zmiennej suma
otrzymujemy wartość 12
, która jest sumą podwojonych liczb parzystych z oryginalnej tablicy. Taki styl programowania (łączenie operacji na tablicach) jest czytelny i często spotykany, zwłaszcza w przetwarzaniu danych. Pozwala opisać co chcemy zrobić z kolekcją danych w kolejnych krokach, bez pisania ręcznych pętli i liczników.