W Parku Jurajskim naukowcy często muszą przekształcać dane z jednej formy w drugą - tłumaczyć sekwencje DNA na atrybuty dinozaurów, konwertować pomiary między różnymi systemami czy analizować zachowania zwierząt. Podobnie w JavaScript, konwersja typów i operacje na typach danych to podstawowe umiejętności, które pozwalają na efektywne manipulowanie danymi.
JavaScript jest językiem dynamicznie typowanym, co oznacza, że automatycznie konwertuje typy danych, gdy jest to potrzebne do wykonania operacji. To jak automatyczne systemy adaptacyjne w Parku Jurajskim, które dostosowują się do zmian warunków - czasami wygodne, ale czasami zaskakujące.
Kiedy JavaScript wykonuje operacje arytmetyczne, próbuje automatycznie konwertować wartości na liczby:
1// String + Number -> String (konkatenacja)
2"Velociraptor " + 3; // "Velociraptor 3"
3
4// String - Number -> Number
5"50" - 10; // 40 (string "50" zostaje skonwertowany na liczbę)
6
7// String * Number -> Number
8"5" * 4; // 20 (string "5" zostaje skonwertowany na liczbę)
9
10// String / Number -> Number
11"20" / 5; // 4 (string "20" zostaje skonwertowany na liczbę)Zauważ szczególny przypadek operatora
+, który dla stringów działa jako operator konkatenacji (łączenia), a nie dodawania. To jak obserwacja, że velociraptory zawsze polują w stadach - wiedza, która może uratować życie!Kiedy porównujemy wartości różnych typów za pomocą operatora
== (równość nieścisła), JavaScript próbuje je skonwertować na wspólny typ:1// Number == String
25 == "5"; // true (string "5" zostaje skonwertowany na liczbę 5)
3
4// Boolean == Number
5true == 1; // true (true zostaje skonwertowany na 1)
6false == 0; // true (false zostaje skonwertowany na 0)
7
8// null == undefined
9null == undefined; // true (specjalny przypadek w specyfikacji)
10
11// Object == Primitive
12[1] == 1; // true ([1] zostaje skonwertowany na "1", a potem na 1)Takie zachowanie przypomina "intuicyjne" obserwacje w parku - "Ta zagroda wygląda tak samo jak tamta" - które mogą być mylące, jeśli nie znasz szczegółów.
Operatory logiczne
&&, || i ! mogą pracować z wartościami każdego typu, konwertując je niejawnie na boolean:1// Operator ! (negacja) konwertuje wartość na boolean i neguje ją
2!0; // true (0 zostaje skonwertowane na false, a potem znegowane)
3!"Dino"; // false ("Dino" zostaje skonwertowane na true, a potem znegowane)
4
5// Operatory && i || mogą zwracać oryginalne wartości, a nie tylko boolean
6"T-Rex" && "Raptor"; // "Raptor" (jeśli pierwszy operand jest truthy, zwraca drugi)
70 || "Backup"; // "Backup" (jeśli pierwszy operand jest falsy, zwraca drugi)Ta logika jest jak protokół postępowania w parku: "Jeśli ogrodzenie działa ORAZ jest wysokie, dinozaur nie ucieknie" - obydwa warunki muszą być spełnione, aby całe zdanie było prawdziwe.
Aby uniknąć nieoczekiwanych wyników niejawnej konwersji, często warto jawnie skonwertować wartości na pożądany typ. To jak precyzyjne protokoły laboratoryjne w Parku Jurajskim - dokładnie określone procedury dające przewidywalne rezultaty.
1// Metoda #1: Funkcja String()
2String(42); // "42"
3String(true); // "true"
4String(null); // "null"
5String(undefined); // "undefined"
6String({ name: "T-Rex" }); // "[object Object]"
7String([1, 2, 3]); // "1,2,3"
8
9// Metoda #2: Metoda toString()
10(42).toString(); // "42"
11true.toString(); // "true"
12[1, 2, 3].toString(); // "1,2,3"
13// null.toString(); // Błąd! Null i undefined nie mają metody toString()
14
15// Metoda #3: Konkatenacja z pustym stringiem
1642 + ""; // "42"
17true + ""; // "true"
18null + ""; // "null"1// Metoda #1: Funkcja Number()
2Number("42"); // 42
3Number("42px"); // NaN (nie można skonwertować całego stringa na liczbę)
4Number(true); // 1
5Number(false); // 0
6Number(null); // 0
7Number(undefined); // NaN
8Number([1]); // 1 (jednolementowa tablica jest konwertowana na jej element)
9Number([1, 2]); // NaN (tablica z wieloma elementami nie może być konwertowana)
10
11// Metoda #2: Funkcje parseInt() i parseFloat()
12parseInt("42"); // 42
13parseInt("42px"); // 42 (konwertuje, dopóki napotka nieprawidłowy znak)
14parseInt("3.14"); // 3 (konwertuje tylko część całkowitą)
15parseFloat("3.14"); // 3.14 (konwertuje część zmiennoprzecinkową)
16
17// Metoda #3: Operatory unarny + i -
18+"42"; // 42
19-"42"; // -42
20+true; // 11// Metoda #1: Funkcja Boolean()
2Boolean(42); // true
3Boolean(0); // false
4Boolean(""); // false
5Boolean("text"); // true
6Boolean(null); // false
7Boolean(undefined); // false
8Boolean({}); // true (każdy obiekt jest truthy)
9
10// Metoda #2: Podwójna negacja !!
11!!42; // true
12!!0; // false
13!!"text"; // true
14!!null; // falsePoniższe tabele są jak tablice genetyczne dinozaurów w parku - pokazują, co dokładnie dzieje się podczas konwersji między różnymi typami.
| Typ wejściowy | Przykład wejściowy | Wynik konwersji na String | |--------------|-------------------|--------------------------| | Number | 42 | "42" | | Boolean | true | "true" | | null | null | "null" | | undefined | undefined | "undefined" | | Array | [1, 2, 3] | "1,2,3" | | Object | {name: "Rex"} | "[object Object]" | | Function | function(){} | "function(){}" |
| Typ wejściowy | Przykład wejściowy | Wynik konwersji na Number | |--------------|-------------------|--------------------------| | String | "42" | 42 | | String | " 42 " | 42 (białe znaki są usuwane) | | String | "42px" | NaN | | String | "" | 0 (pusty string) | | Boolean | true | 1 | | Boolean | false | 0 | | null | null | 0 | | undefined | undefined | NaN | | Array | [] | 0 (pusta tablica) | | Array | [5] | 5 (tablica z jednym elementem) | | Array | [1, 2] | NaN (wiele elementów) | | Object | {} | NaN |
| Typ wejściowy | Przykład wejściowy | Wynik konwersji na Boolean | |--------------|-------------------|---------------------------| | Number | 42 | true | | Number | 0 | false | | Number | NaN | false | | String | "tekst" | true | | String | "" | false | | null | null | false | | undefined | undefined | false | | Object | {} (jakikolwiek) | true | | Array | [] (jakakolwiek) | true |
Teraz zajmijmy się szczegółowymi operacjami na poszczególnych typach danych. To jak specjalistyczne procedury dla różnych gatunków dinozaurów.
Stringi w JavaScript oferują bogaty zestaw metod do manipulacji tekstem:
1const parkName = "Jurassic Park";
2
3// Długość stringa
4parkName.length; // 13
5
6// Dostęp do poszczególnych znaków
7parkName[0]; // "J"
8parkName.charAt(0); // "J" (alternatywna metoda)
9
10// Wyszukiwanie podciągów
11parkName.indexOf("Park"); // 9 (indeks, gdzie zaczyna się "Park")
12parkName.startsWith("Jur"); // true
13parkName.endsWith("Park"); // true
14parkName.includes("assic"); // true
15
16// Zmiana wielkości liter
17parkName.toUpperCase(); // "JURASSIC PARK"
18parkName.toLowerCase(); // "jurassic park"
19
20// Wycinanie fragmentów
21parkName.substring(0, 8); // "Jurassic"
22parkName.slice(-4); // "Park" (liczy od końca)
23
24// Zastępowanie fragmentów
25parkName.replace("Jurassic", "Dinosaur"); // "Dinosaur Park"
26
27// Podział stringa na tablicę
28parkName.split(" "); // ["Jurassic", "Park"]
29
30// Usuwanie białych znaków
31" Danger Zone ".trim(); // "Danger Zone"
32
33// Łączenie stringów
34"Welcome".concat(" to ", parkName); // "Welcome to Jurassic Park"
35
36// Powtarzanie stringa
37"Roar! ".repeat(3); // "Roar! Roar! Roar! "JavaScript oferuje wiele możliwości pracy z liczbami, zarówno bezpośrednio na wartościach, jak i przez obiekt
Math:1// Podstawowe operacje arytmetyczne
25 + 3; // 8 (dodawanie)
35 - 3; // 2 (odejmowanie)
45 * 3; // 15 (mnożenie)
55 / 3; // 1.6666666666666667 (dzielenie)
65 % 3; // 2 (modulo - reszta z dzielenia)
75 ** 3; // 125 (potęgowanie - 5 do potęgi 3)
8
9// Inkrementacja i dekrementacja
10let dinoCount = 5;
11dinoCount++; // dinoCount jest teraz 6
12dinoCount--; // dinoCount z powrotem do 5
13
14// Operatory przypisania i operacji
15dinoCount += 3; // To samo co: dinoCount = dinoCount + 3
16dinoCount *= 2; // To samo co: dinoCount = dinoCount * 2
17
18// Obiekt Math do zaawansowanych operacji
19Math.abs(-5); // 5 (wartość bezwzględna)
20Math.round(4.7); // 5 (zaokrąglenie do najbliższej liczby całkowitej)
21Math.ceil(4.1); // 5 (zaokrąglenie w górę)
22Math.floor(4.9); // 4 (zaokrąglenie w dół)
23Math.max(3, 7, 2, 4); // 7 (znajdowanie maksimum)
24Math.min(3, 7, 2, 4); // 2 (znajdowanie minimum)
25Math.pow(2, 3); // 8 (potęgowanie - 2 do potęgi 3)
26Math.sqrt(16); // 4 (pierwiastek kwadratowy)
27Math.sin(0); // 0 (sinus)
28Math.cos(0); // 1 (cosinus)
29Math.random(); // Losowa liczba od 0 (włącznie) do 1 (wyłącznie)
30
31// Generowanie losowej liczby z zakresu
32function getRandom(min, max) {
33 return Math.floor(Math.random() * (max - min + 1)) + min;
34}
35getRandom(1, 10); // Losowa liczba od 1 do 10
36
37// Formatowanie liczb
38const height = 5.678934;
39height.toFixed(2); // "5.68" (dwa miejsca po przecinku, zwraca string)
40height.toPrecision(3); // "5.68" (trzy znaczące cyfry, zwraca string)
41height.toString(2); // "101.10101011100001010001111" (reprezentacja binarna)
42(1234567).toLocaleString(); // "1,234,567" (formatowanie z separatorami, zależy od locale)Chociaż typ boolean ma tylko dwie możliwe wartości, istnieje wiele operacji logicznych:
1// Operatory porównania
25 > 3; // true (większe niż)
35 >= 5; // true (większe lub równe)
43 < 5; // true (mniejsze niż)
53 <= 3; // true (mniejsze lub równe)
65 == "5"; // true (równość nieścisła - różne typy są konwertowane)
75 === "5"; // false (równość ścisła - typy muszą być zgodne)
85 != "6"; // true (nierówność nieścisła)
95 !== "5"; // true (nierówność ścisła)
10
11// Operatory logiczne
12true && true; // true (AND logiczne - oba warunki muszą być prawdziwe)
13true && false; // false
14true || false; // true (OR logiczne - co najmniej jeden warunek musi być prawdziwy)
15false || false; // false
16!true; // false (NOT logiczne - negacja)
17!false; // true
18
19// Złożone warunki
20const isSafe = fenceActive && powerLevel > 5000 && !stormApproaching;
21const needsEvacuation = alarmTriggered || dinoEscaped || stormStrength > 8;Pomimo ograniczonych możliwości bezpośrednich operacji na
null i undefined, istnieją użyteczne wzorce pracy z nimi:1// Operator trójskładnikowy do domyślnych wartości
2const visitorName = userName || "Gość"; // Użycie userName, jeśli istnieje, w przeciwnym razie "Gość"
3
4// Operator nullish coalescing (??) - dostępny w nowszych wersjach JS
5const count = dinoCount ?? 0; // Użycie dinoCount, jeśli nie jest null/undefined, w przeciwnym razie 0
6
7// Optional chaining (?.) - dostępny w nowszych wersjach JS
8const species = dinosaur?.species; // Bezpiecznie dostęp do właściwości, nawet jeśli dinosaur jest null/undefinedNiektóre konwersje typów w JavaScript mogą być zaskakujące, podobnie jak nietypowe zachowania dinozaurów po burzy:
1// Dziwne przypadki równości nieścisłej (==)
2[] == ''; // true (pusta tablica jest konwertowana na pusty string)
3[1] == 1; // true (tablica [1] jest konwertowana na string "1", a potem na liczbę 1)
4[1, 2] == '1,2'; // true (tablica [1, 2] jest konwertowana na string "1,2")
5true == '1'; // true (true jest konwertowane na 1, a '1' na 1)
6false == '0'; // true (false jest konwertowane na 0, a '0' na 0)
7null == undefined; // true (specjalny przypadek w specyfikacji)
8'' == 0; // true (pusty string jest konwertowany na 0)
9
10// Operacje z NaN
11NaN == NaN; // false (NaN nie jest równe niczemu, nawet samemu sobie)
12Number.isNaN(NaN); // true (właściwy sposób sprawdzania NaN)
13
14// Operacje z obiektami
15const obj1 = {}, obj2 = {};
16obj1 == obj2; // false (obiekty są porównywane przez referencję, nie zawartość)
17obj1 === obj2; // false (obiekty są porównywane przez referencję, nie zawartość)
18
19// Operacje na tablicach
20[] + []; // "" (tablice są konwertowane na stringi, a potem łączone)
21[] + {}; // "[object Object]" (obiekt jest konwertowany na string)
22{} + []; // 0 (puste {} jest traktowane jako blok kodu, a +[] jest konwertowane na liczbę)Aby uniknąć niespodzianek przy pracy z typami danych, warto stosować następujące zasady - są jak protokoły bezpieczeństwa w Parku Jurajskim:
1// ŹLE
2if (dinoCount == "5") { // Zadziała, ale może prowadzić do błędów
3
4// DOBRZE
5if (dinoCount === 5) { // Wyraźnie wskazuje, że dinoCount powinno być liczbą1// ŹLE
2const totalVisitors = visitorCount + newVisitors; // Może dawać niepoprawne wyniki, jeśli któraś zmienna jest stringiem
3
4// DOBRZE
5const totalVisitors = Number(visitorCount) + Number(newVisitors); // Jawna konwersja na liczby1// ŹLE
2if (typeof height == "number" && !isNaN(height)) { // Działa, ale jest nieprecyzyjne
3
4// DOBRZE
5function isValidNumber(value) {
6 return typeof value === 'number' && !isNaN(value) && isFinite(value);
7}
8if (isValidNumber(height)) {1// ŹLE
2if (dinosaur == otherDinosaur) { // Porównuje referencje, nie zawartość
3
4// DOBRZE
5function isEqual(obj1, obj2) {
6 return JSON.stringify(obj1) === JSON.stringify(obj2); // Dla prostych obiektów
7}
8if (isEqual(dinosaur, otherDinosaur)) {1// ZAMIAST
2const safeVisitorCount = visitorCount !== null && visitorCount !== undefined ? visitorCount : 0;
3const speciesName = dinosaur && dinosaur.species ? dinosaur.species : "Nieznany";
4
5// LEPIEJ (w nowszym JS)
6const safeVisitorCount = visitorCount ?? 0;
7const speciesName = dinosaur?.species ?? "Nieznany";Poniższy przykład pokazuje, jak zastosować operacje na typach danych w praktycznym scenariuszu:
1// Dane przychodzące z różnych źródeł (symulacja API, formularzy, plików CSV)
2const rawDinoData = [
3 { id: "1", name: "Rexy", species: "T-Rex", height: "5.6", dangerous: "true", lastFed: "2023-05-15T08:30:00" },
4 { id: 2, name: "Blue", species: "Velociraptor", height: "1.8", dangerous: true, lastFed: "2023-05-15T09:15:00" },
5 { id: "3", name: "Spike", species: "Stegosaurus", height: 4.2, dangerous: "false", lastFed: null },
6 { id: "4", name: "Cera", species: "Triceratops", height: "unknown", dangerous: false, lastFed: "invalid-date" }
7];
8
9// Funkcja do normalizacji i walidacji danych
10function processDinosaurData(dinoList) {
11 // Przygotowanie znormalizowanej tablicy
12 const processedData = [];
13
14 // Przetworzenie każdego elementu
15 for (let i = 0; i < dinoList.length; i++) {
16 const dino = dinoList[i];
17
18 // Normalizacja ID
19 const id = Number(dino.id);
20 if (isNaN(id) || id <= 0) {
21 console.warn(`Pominięto dinozaura z nieprawidłowym ID: ${dino.id}`);
22 continue;
23 }
24
25 // Normalizacja imienia
26 const name = String(dino.name).trim();
27 if (name === "") {
28 console.warn(`Pominięto dinozaura bez imienia, ID: ${id}`);
29 continue;
30 }
31
32 // Normalizacja gatunku
33 const species = String(dino.species).trim() || "Nieznany";
34
35 // Normalizacja wysokości
36 let height;
37 if (dino.height === "unknown" || dino.height === undefined) {
38 height = null;
39 } else {
40 height = Number(dino.height);
41 if (isNaN(height)) {
42 console.warn(`Nieprawidłowa wysokość dla ${name}, ustawiono null`);
43 height = null;
44 }
45 }
46
47 // Normalizacja flagi niebezpieczny
48 let dangerous;
49 if (typeof dino.dangerous === 'string') {
50 dangerous = dino.dangerous.toLowerCase() === 'true';
51 } else {
52 dangerous = Boolean(dino.dangerous);
53 }
54
55 // Normalizacja daty ostatniego karmienia
56 let lastFed = null;
57 if (dino.lastFed) {
58 const date = new Date(dino.lastFed);
59 if (!isNaN(date.getTime())) {
60 lastFed = date;
61 } else {
62 console.warn(`Nieprawidłowa data karmienia dla ${name}`);
63 }
64 }
65
66 // Dodatkowe obliczenia
67 const securityLevel = dangerous ? (height > 3 ? "Wysoki" : "Średni") : "Niski";
68 const daysFromLastFeed = lastFed ? Math.floor((new Date() - lastFed) / (1000 * 60 * 60 * 24)) : null;
69
70 // Dodanie znormalizowanego dinozaura do wynikowej tablicy
71 processedData.push({
72 id,
73 name,
74 species,
75 height,
76 dangerous,
77 lastFed,
78 securityLevel,
79 daysFromLastFeed,
80 needsFeeding: daysFromLastFeed === null || daysFromLastFeed > 1
81 });
82 }
83
84 return processedData;
85}
86
87// Używanie funkcji
88const normalizedDinos = processDinosaurData(rawDinoData);
89console.log(normalizedDinos);
90
91// Dodatkowe przetwarzanie danych
92const dangerousDinos = normalizedDinos.filter(dino => dino.dangerous);
93const avgHeight = normalizedDinos
94 .filter(dino => dino.height !== null)
95 .reduce((sum, dino) => sum + dino.height, 0) / normalizedDinos.length;
96const dinosBySecurityLevel = normalizedDinos.reduce((groups, dino) => {
97 groups[dino.securityLevel] = groups[dino.securityLevel] || [];
98 groups[dino.securityLevel].push(dino);
99 return groups;
100}, {});Ten przykład pokazuje, jak radzić sobie z różnymi typami danych, konwertować je, walidować i normalizować - wszystko to są podstawowe operacje, których naukowcy w Parku Jurajskim musieliby dokonywać na codziennych danych.
Konwersja typów i operacje na typach danych w JavaScript to obszerne zagadnienie, które wymaga zrozumienia wielu niuansów. Kluczowe aspekty to:
Niejawna konwersja - JavaScript automatycznie konwertuje typy w wielu kontekstach
Jawna konwersja - Kontrolowane przekształcanie typów
String(), Number(), Boolean() - funkcje konwertującetoString(), parseInt(), parseFloat() - metody i funkcje specjalizowane!! - szybka konwersja na booleanOperacje na poszczególnych typach
Najlepsze praktyki
Podobnie jak w Parku Jurajskim, gdzie różne gatunki dinozaurów wymagają różnych protokołów postępowania, tak w JavaScript różne typy danych wymagają odpowiedniego podejścia. Znajomość zasad konwersji i operacji na typach pozwala na skuteczne i bezpieczne manipulowanie danymi - podstawę każdej aplikacji JavaScript.
W następnej lekcji przyjrzymy się operatorom arytmetycznym i przypisania, które pozwalają na przeprowadzanie bardziej zaawansowanych kalkulacji - niezbędnych dla zarządzania zasobami w tak złożonym przedsięwzięciu jak Park Jurajski.