Kurs JavaScript i TypeScript · Moduł 2: Funkcje, tablice i obiekty
Template literals i metody stringów
W tej lekcji8
W laboratorium Parku Jurajskiego co godzinę powstają raporty: gatunek, waga, strefa, stan zdrowia. Sklejanie ich plusami szybko zamienia się w gąszcz cudzysłowów, a dane z czujników przychodzą z nadmiarowymi spacjami i w przypadkowej wielkości liter. Template literals i metody stringów to narzędzia, które porządkują tekst: od wstawiania zmiennych po przeszukiwanie i przekształcanie łańcuchów znaków.
Template literals - podstawy
Template literals to stringi otoczone grawisami, czyli odwróconymi apostrofami, a nie zwykłymi cudzysłowami. Wyrażenie JavaScript wstawiasz do nich składnią ${...}:
1const species = 'T-Rex';
2const weight = 8000;
3
4// Stary sposób - konkatenacja
5const old = 'Gatunek: ' + species + ', waga: ' + weight + ' kg';
6
7// Template literal - znacznie czytelniej!
8const report = `Gatunek: ${species}, waga: ${weight} kg`;
9console.log(report); // "Gatunek: T-Rex, waga: 8000 kg"Obie zmienne zawierają ten sam tekst, ale w szablonie od razu widać kształt zdania. To dlatego polecam template literals zawsze, gdy w tekście pojawia się choć jedna zmienna.
Wyrażenia wewnątrz ${}
Wewnątrz ${} można umieścić dowolne wyrażenie JavaScript, nie tylko zmienną. Działa tu operator warunkowy, arytmetyka, wywołanie metody, a nawet zagnieżdżony template literal:
1const dino = { name: 'Rex', health: 85 };
2
3console.log(`Status: ${dino.health > 50 ? 'stabilny' : 'krytyczny'}`);
4// "Status: stabilny"
5
6console.log(`Zdrowie: ${dino.health}%`);
7// "Zdrowie: 85%"
8
9console.log(`Podwójna dawka: ${dino.health * 2} jednostek`);
10// "Podwójna dawka: 170 jednostek"
11
12console.log(`Imię: ${dino.name.toUpperCase()}`);
13// "Imię: REX"JavaScript najpierw oblicza wyrażenie, potem zamienia wynik na tekst i wkleja go w miejsce ${}. Obiekt dino pozostaje przy tym nietknięty.
Wieloliniowe stringi
Template literals zachowują formatowanie wieloliniowe, więc nie trzeba wstawiać \n:
1const dinoCard = `
2╔════════════════════════╗
3║ Gatunek: T-Rex ║
4║ Strefa: A ║
5║ Status: Aktywny ║
6╚════════════════════════╝
7`;
8console.log(dinoCard);Każdy enter i każda spacja wewnątrz grawisów trafiają do wyniku, łącznie z pustą linią na początku.
Tagged templates
Tagged templates pozwalają przetworzyć szablon funkcją. Funkcja "tagująca" dostaje tablicę stałych fragmentów tekstu oraz wartości wyrażeń, a fragmentów jest zawsze o jeden więcej niż wartości:
1function highlight(strings, ...values) {
2 let result = '';
3 strings.forEach((str, i) => {
4 result += str;
5 if (i < values.length) {
6 result += '**' + values[i] + '**';
7 }
8 });
9 return result;
10}
11
12const name = 'Rex';
13const zone = 'A';
14const tagged = highlight`Dinozaur ${name} w strefie ${zone}`;
15console.log(tagged);
16// "Dinozaur **Rex** w strefie **A**"Zwróć uwagę, że tag wywołujemy bez nawiasów: nazwa funkcji stoi tuż przed grawisem. Praktyczne zastosowanie to budowanie zapytań do bazy, w którym wartości trafiają osobno od treści zapytania:
1function sql(strings, ...values) {
2 const query = strings.join('?');
3 return { query, params: values };
4}
5
6const zoneId = 'A';
7const minHealth = 80;
8const query = sql`SELECT * FROM dinos WHERE zone = ${zoneId} AND health > ${minHealth}`;
9// { query: "SELECT * FROM dinos WHERE zone = ? AND health > ?", params: ['A', 80] }Wartości nie zostały wklejone do tekstu zapytania, tylko odłożone do params. Na tej zasadzie działają zapytania parametryzowane, które chronią bazę przed atakiem SQL injection.
Metody stringów - wyszukiwanie
Zacznijmy od wyszukiwania fragmentów w sekwencji DNA. Każda z tych metod tylko czyta tekst i zwraca odpowiedź:
1const dnaSequence = 'ATCG-TTGA-ACGT-TTGA';
2
3// indexOf() - zwraca indeks pierwszego wystąpienia (lub -1)
4console.log(dnaSequence.indexOf('TTGA')); // 5
5console.log(dnaSequence.indexOf('XXXX')); // -1
6console.log(dnaSequence.lastIndexOf('TTGA')); // 15 - ostatnie wystąpienie
7
8// includes() - sprawdza czy string zawiera fragment (boolean)
9console.log(dnaSequence.includes('ACGT')); // true
10console.log(dnaSequence.includes('ZZZZ')); // false
11
12// startsWith() / endsWith() - sprawdza początek/koniec
13console.log(dnaSequence.startsWith('ATCG')); // true
14console.log(dnaSequence.endsWith('TTGA')); // true
15console.log(dnaSequence.startsWith('TTGA', 5)); // true - od pozycji 5
16
17// match() z wyrażeniem regularnym - wszystkie dopasowania
18console.log(dnaSequence.match(/TTGA/g)); // ['TTGA', 'TTGA']Wszystkie te metody rozróżniają wielkość liter, więc includes('acgt') zwróci false. Gdy pytasz tylko "czy jest?", wybierz includes(), bo czyta się jak zdanie, a indexOf() zostaw na sytuacje, gdy potrzebujesz pozycji.
Metody stringów - wycinanie i dzielenie
Wycinanie przydaje się, gdy z pełnej nazwy okazu potrzebujesz tylko części:
1const specimen = 'Tyrannosaurus Rex';
2
3// slice(start, end) - wycina fragment stringa
4console.log(specimen.slice(0, 13)); // "Tyrannosaurus"
5console.log(specimen.slice(14)); // "Rex"
6console.log(specimen.slice(-3)); // "Rex" (od końca)
7
8// split() - dzieli string na tablicę
9const zones = 'A-B-C-D'.split('-');
10console.log(zones); // ['A', 'B', 'C', 'D']
11
12const words = 'Park Jurajski jest super'.split(' ');
13console.log(words); // ['Park', 'Jurajski', 'jest', 'super']slice nie obejmuje znaku o indeksie end, a ujemny indeks liczy od końca, więc slice(-3) to trzy ostatnie znaki. split zwraca tablicę, którą w następnej lekcji rozpakujesz destrukturyzacją.
Metody stringów - transformacje
Surowe dane z czujników trzeba oczyścić. Kluczowa zasada: stringi są niezmienne, więc każda metoda zwraca nowy string:
1const dinoName = ' velociraptor ';
2
3// trim() - usuwa białe znaki z początku i końca
4console.log(dinoName.trim()); // "velociraptor"
5console.log(dinoName.trimStart()); // "velociraptor "
6console.log(dinoName.trimEnd()); // " velociraptor"
7
8// toUpperCase() / toLowerCase()
9console.log('rex'.toUpperCase()); // "REX"
10console.log('T-REX'.toLowerCase()); // "t-rex"
11
12// replace() - zamienia pierwsze wystąpienie
13const text = 'Zone A, Zone B, Zone A';
14console.log(text.replace('Zone', 'Strefa')); // "Strefa A, Zone B, Zone A"
15
16// replaceAll() - zamienia wszystkie wystąpienia
17console.log(text.replaceAll('Zone', 'Strefa')); // "Strefa A, Strefa B, Strefa A"Po tych wywołaniach dinoName wciąż ma spacje, a text wciąż zawiera "Zone". Wynik zapisz w nowej zmiennej, jeśli chcesz go zachować. Różnica między replace i replaceAll dotyczy więc liczby zamian, a nie modyfikacji oryginału.
Metody stringów - dopełnianie
Metody padStart() i padEnd() dopełniają string do zadanej długości, a repeat() powiela go. Razem pozwalają wyrównać tabelę okazów:
1// padStart(length, fillString) - dopełnia od początku
2const dinoId = '42';
3console.log(dinoId.padStart(5, '0')); // "00042"
4console.log(dinoId.padStart(5, '-')); // "---42"
5
6// padEnd(length, fillString) - dopełnia od końca
7const label = 'Rex';
8console.log(label.padEnd(10, '.')); // "Rex......."
9
10// repeat(count) - powiela string
11console.log('-'.repeat(30)); // "------------------------------"
12
13// Praktyczny przykład - formatowanie tabeli
14const specimens = [
15 { id: '1', name: 'T-Rex' },
16 { id: '42', name: 'Velociraptor' },
17 { id: '100', name: 'Triceratops' }
18];
19
20specimens.forEach(s => {
21 const formattedId = s.id.padStart(4, '0');
22 const formattedName = s.name.padEnd(15, ' ');
23 console.log(`ID: ${formattedId} | Gatunek: ${formattedName}`);
24});
25// ID: 0001 | Gatunek: T-Rex
26// ID: 0042 | Gatunek: Velociraptor
27// ID: 0100 | Gatunek: TriceratopsPierwszy argument to docelowa długość całego stringa, a nie liczba dodawanych znaków. Dlatego '42'.padStart(5, '0') dokleja trzy zera.
Łączenie metod (chaining)
Metody stringów można łączyć w łańcuchy, bo każda zwraca nowy string, na którym wywołujesz kolejną:
1const messy = ' park JURAJSKI jest SUPER ';
2const clean = messy.trim().toLowerCase().replace(/\s+/g, ' ');
3console.log(clean); // "park jurajski jest super"
4
5const dinoCode = 'trex-001';
6const formatted = dinoCode.split('-')
7 .map(part => part.toUpperCase())
8 .join('-');
9console.log(formatted); // "TREX-001"/\s+/g to wyrażenie regularne oznaczające dowolny ciąg białych znaków, a flaga g każe zamienić wszystkie wystąpienia. Ten sam wzorzec tworzy przyjazne adresy raportów:
1function formatReport(rawInput) {
2 return rawInput
3 .trim()
4 .toLowerCase()
5 .replace(/\s+/g, '-') // spacje na myślniki
6 .replace(/[^a-z0-9-]/g, '') // usuń znaki inne niż litery, cyfry i myślniki
7 .slice(0, 50); // maksymalnie 50 znaków
8}
9
10console.log(formatReport(' T-Rex Escape!! Zone A '));
11// "t-rex-escape-zone-a"Każdy krok łańcucha robi jedną rzecz, więc łatwo go czytać i poprawiać. Polecam łamać długie łańcuchy tak jak tutaj, jedna metoda w linii.
Praktyka
W piaskownicy czekają szablony raportów, wyszukiwanie w DNA, slice, split, replace i dopełnianie. Zmieniaj argumenty i przewiduj wynik, zanim uruchomisz kod.
Kod do tej lekcji: index.js
1// === TEMPLATE LITERALS I METODY STRINGÓW ===
2
3// 1. Template literals - wstawianie wyrażeń
4const species = 'T-Rex';
5const weight = 8000;
6
7console.log('=== Template Literals ===');
8console.log(`Gatunek: ${species}, waga: ${weight} kg`);
9console.log(`Zdrowie: ${weight > 5000 ? 'duży osobnik' : 'mały osobnik'}`);
10console.log(`Podwójna dawka: ${weight * 0.05} kg`);
11
12// 2. Wieloliniowe stringi
13const card = `
14=== KARTA DINOZAURA ===
15Gatunek: ${species}
16Waga: ${weight} kg
17Status: aktywny
18`;
19console.log(card);
20
21// 3. Metody wyszukiwania
22console.log('=== Wyszukiwanie ===');
23const dna = 'ATCG-TTGA-ACGT-TTGA';
24console.log('indexOf TTGA:', dna.indexOf('TTGA'));
25console.log('includes ACGT:', dna.includes('ACGT'));
26console.log('startsWith ATCG:', dna.startsWith('ATCG'));
27console.log('endsWith TTGA:', dna.endsWith('TTGA'));
28
29// 4. Wycinanie i dzielenie
30console.log('\n=== Slice i Split ===');
31const specimen = 'Tyrannosaurus Rex';
32console.log('slice(0,13):', specimen.slice(0, 13));
33console.log('slice(-3):', specimen.slice(-3));
34console.log('split:', 'A-B-C-D'.split('-'));
35
36// 5. Transformacje
37console.log('\n=== Transformacje ===');
38console.log('trim:', ' rex '.trim());
39console.log('toUpperCase:', 'rex'.toUpperCase());
40console.log('replace:', 'Zone A, Zone B'.replace('Zone', 'Strefa'));
41console.log('replaceAll:', 'Zone A, Zone B'.replaceAll('Zone', 'Strefa'));
42
43// 6. Dopełnianie (padding)
44console.log('\n=== Padding ===');
45console.log('padStart:', '42'.padStart(5, '0'));
46console.log('padEnd:', 'Rex'.padEnd(10, '.'));
47
48// 7. Chaining metod
49console.log('\n=== Chaining ===');
50const messy = ' park JURAJSKI ';
51const clean = messy.trim().toLowerCase();
52console.log('Wynik:', clean);Pamiętaj: template literal to etykieta z wolnymi polami na dane, a metody stringów to skalpele laboranta, które nigdy nie tną oryginalnej próbki, tylko tworzą jej kopię.
Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Jakiego znaku używamy do otaczania template literals w JavaScript?
2. Jaka składnia pozwala wstawić wyrażenie JavaScript wewnątrz template literal?
3. Co zwróci 'Tyrannosaurus Rex'.slice(-3)?
4. Jaka jest różnica między replace() a replaceAll() w JavaScript?
5. Co zwróci 'A-B-C-D'.split('-')?
Zadania praktyczne w grze
- Klikanie w kolejności
Ułóż wywołanie, które sprawdza, czy dnaSequence zawiera fragment 'ACGT':
- Układanie w poziomie
Ułóż elementy wywołania padStart() do dopełnienia ID zerami: