Deklaracje var, let i const oraz różnica w zasięgu, typy proste i złożone, operator typeof z pułapką typeof null, konwersje napisów na liczby, NaN, operatory arytmetyczne, logiczne i porównania, w tym == kontra ===. Lekcja 11.2 kursu INF.03.
Zmienna to nazwa, pod którą skrypt trzyma jedną wartość: liczbę, napis, wartość logiczną albo całą tablicę. Typu nie deklaruje się z góry, bo JavaScript nadaje go po wartości, która do zmiennej trafiła, i zmienia w locie przy kolejnym przypisaniu. W części pisemnej wracają pytania z ramką kodu („co wyświetli przedstawiony skrypt", „jaką wartość będzie miała zmienna", „która nazwa zmiennej jest poprawna", „kiedy wynikiem jest NaN", „czym różni się == od ==="). W zadaniu praktycznym liczba wpisana w formularz przychodzi do skryptu jako napis, więc bez konwersji i bez porównań ta część zadania nie ruszy z miejsca.
Deklaracja: var, let i const
Deklaracja to wprowadzenie nazwy zmiennej do skryptu słowem kluczowym. JavaScript ma trzy takie słowa, a różnią się zasięgiem, czyli obszarem kodu, w którym nazwa jest widoczna, oraz tym, czy wolno przypisać do niej wartość ponownie.
›
var (ZMIENNA) to słowo najstarsze: zmienna nim zadeklarowana jest widoczna w całej funkcji, klamra nie jest dla niej granicą, a nazwę wolno zadeklarować drugi raz. W arkuszach CKE i w bankach pytań stoi wszędzie, choć nowy kod pisze się bez niej.
›
let (NIECH) ogranicza widoczność do bloku, czyli do klamer z deklaracją. Wartość wolno zmieniać dowolnie często.
›
const (STAŁA) ma ten sam zasięg co let, ale wartość nadaje się jej raz, przy deklaracji. Ponowne przypisanie kończy się błędem TypeError.
var jezyk = "JavaScript";
let licznik = 0;
const VAT = 0.23;
licznik = licznik + 1;
console.log(jezyk);
console.log(licznik);
console.log(VAT);
Różnicę zasięgu widać dopiero przy deklaracji wewnątrz klamry.
let zostaje w klamrze
var nie zna klamry
Odwołanie do tylkoTutaj spoza klamry kończy się błędem ReferenceError. Zmienna z var jest za to widoczna już przed swoją deklaracją i ma wtedy undefined, więc zamiast czytelnego błędu daje ciche undefined w wyniku. Stąd na co dzień: const dla wartości stałej, let dla reszty.
Nazwa zmiennej zaczyna się od litery, podkreślenia albo dolara, dalej mogą stać cyfry. Spacja, dywiz, znak # i cyfra na początku są niedozwolone, a w odpowiedziach egzaminacyjnych poprawna jest zawsze wersja bez polskich liter, czyli imie2, a nie imię2. Wielkość liter ma znaczenie: cena i Cena to różne zmienne. Nazw zarezerwowanych przez język (let, new, class, if) użyć nie wolno, a nazwy złożone zapisuje się z wielką literą na styku słów: cenaBrutto.
Pułapka: const blokuje ponowne przypisanie do nazwy, a nie zmianę zawartości: elementy tablicy zadeklarowanej jako const wolno podmieniać.
Typy danych i operator typeof
Typ to rodzaj wartości, który decyduje, co wolno z nią zrobić: liczby się dodaje, napisy skleja, wartości logiczne sprawdza w warunku. Typów prostych jest pięć, a wszystko, co trzyma wiele wartości naraz, jest obiektem.
Typ
Co trzyma
Przykład deklaracji
number
liczby całkowite i ułamkowe, jeden wspólny typ
let a = 19.99;
string
napis w cudzysłowie albo w apostrofach
let b = "Mysz";
boolean
jedną z dwóch wartości: true albo false
let c = true;
undefined
nic, bo zmienna nie dostała jeszcze wartości
let d;
null
celowo wpisany brak wartości
let e = null;
object
wiele wartości naraz, w tym tablicę
let f = [45, 120];
Część ułamkową oddziela kropka, więc 19,99 jest błędem składni. Liczba całkowita i ułamkowa to ten sam typ number, choć odpowiedzi w arkuszach nazywają je osobno: „liczbowy, całkowity" i „zmiennoprzecinkowy". Napis wolno zapisać w cudzysłowie albo w apostrofach i obie postacie znaczą to samo, inaczej niż przy zmiennych i operatorach w PHP. Tablicami i obiektami zajmuje się osobna lekcja. typeof (JAKIEGO TYPU) to operator zwracający nazwę typu jako napis.
const cena = 19.99;
const nazwa = "Mysz";
const dostepny = true;
let rabat;
console.log(typeof cena);
console.log(typeof nazwa);
console.log(typeof dostepny);
console.log(typeof rabat);
number
string
boolean
undefined
Wartość undefined nadaje sam język zmiennej, która nic nie dostała, a null wpisuje programista. Trzy wyniki typeof trzeba znać na pamięć.
console.log(typeof null);
console.log(typeof [1, 2, 3]);
console.log(typeof NaN);
typeof null daje object, choć null żadnym obiektem nie jest: to błąd z pierwszej wersji języka, zostawiony dla zgodności ze starymi stronami. Tablica jest odmianą obiektu, a NaN, czyli wynik nieudanego działania na liczbach, ma typ number.
Konwersje: kiedy napis staje się liczbą
Konwersja to zamiana wartości na inny typ. JavaScript robi ją sam, bez pytania, a wynik zależy od tego, który operator dostał napis.
console.log("5" + 3);
console.log("5" - 3);
console.log("5" * "2");
console.log(3 + 4 + "5");
Operator + ma dwa zadania: dodaje liczby i skleja napisy. Gdy po którejkolwiek stronie stoi napis, wygrywa sklejanie, więc "5" + 3 daje 53. Pozostałe operatory arytmetyczne sklejać nie potrafią, więc zamieniają napis na liczbę i "5" - 3 daje 2. W ostatnim wierszu 3 + 4 policzyło się jako liczby, bo napis pojawił się dopiero później.
Konwersję lepiej wykonać samemu, jedną z czterech funkcji.
Number("12")
12
cały napis musi być liczbą
Number("12abc")
NaN
jeden znak spoza liczby psuje całość
Number("")
0
pusty napis staje się zerem
parseInt("12abc")
12
czyta od początku do pierwszego niepasującego znaku
parseInt("3.9")
3
ucina część ułamkową, nie zaokrągla
parseFloat("3.9 kg")
3.9
zachowuje część ułamkową
String(12)
"12"
liczba jako napis
const wpisane = "3.9 kg";
console.log(parseInt(wpisane));
console.log(parseFloat(wpisane));
console.log(parseInt(3 / 2));
console.log(Number(wpisane));
NaN (NIE LICZBA) to wartość, którą JavaScript oddaje, gdy działanie arytmetyczne nie miało z czego policzyć wyniku, najczęściej dlatego, że w zmiennej był napis. NaN nie jest równe niczemu, także samemu sobie, więc sprawdza się je funkcją isNaN (CZY NIE LICZBA).
const ilosc = Number("dwa");
console.log(ilosc);
console.log(NaN === NaN);
console.log(isNaN(ilosc));
console.log(isNaN("12"));
W walidacji ważny jest ostatni wiersz: isNaN("12") daje false, bo napis z samych cyfr da się zamienić na liczbę, i dopiero litera w polu zwraca true.
Pułapka: parseInt czyta napis od początku i oddaje to, co zdążył przeczytać, a Number wymaga, żeby liczbą był CAŁY napis. Dlatego parseInt("12abc") daje 12, a Number("12abc") daje NaN.
Operatory arytmetyczne, przypisania i inkrementacja
Operator to znak albo para znaków wykonujących działanie na jednej lub dwóch wartościach.
Operator
Znaczy
Przykład
Wynik
+
dodawanie liczb, sklejanie napisów
7 + 3
10
/
dzielenie, wynik bywa ułamkiem
10 / 4
2.5
%
reszta z dzielenia
7 % 3
1
Dzielenie zawsze zwraca wynik dokładny, więc 10 / 4 daje 2.5, a nie 2, a sam iloraz całkowity wyciąga dopiero parseInt(10 / 4) albo Math.floor(10 / 4). Operator % liczy resztę, więc liczba % 2 === 0 to test parzystości. Potęgę zapisuje się dwiema gwiazdkami albo starszym Math.pow, i obie postacie dają ten sam wynik.
console.log(10 / 4);
console.log(7 % 3);
console.log(2 ** 3);
console.log(Math.pow(2, 3));
Przypisanie złożone skraca zapis, w którym zmienna zmienia własną wartość: suma += 5 znaczy suma = suma + 5, tak samo -=, *=, /= i %=.
let suma = 10;
suma += 5;
suma *= 2;
suma %= 7;
console.log(suma);
Wynik: 2. Kolejno 15, potem 30, a reszta z dzielenia 30 przez 7 to 2.
++ (inkrementacja) zwiększa zmienną o jeden, -- (dekrementacja) zmniejsza. W osobnym wierszu obie postacie znaczą to samo co a = a + 1, ale w wyrażeniu decyduje miejsce znaku: przed nazwą najpierw zmienia zmienną i oddaje nową wartość, po nazwie oddaje wartość STARĄ i zmienia zmienną potem.
let a = 5;
const b = a++;
const c = ++a;
console.log(a);
console.log(b);
console.log(c);
a++ oddaje do b piątkę i ustawia a na 6, a ++a podnosi a do 7 i dopiero tę siódemkę wpisuje do c.
Porównania, operatory logiczne i wybór jednej z dwóch wartości
Porównanie to operator, który z dwóch wartości robi true albo false.
Operator
Znaczy
Przykład
Wynik
==
równe po konwersji, bez patrzenia na typ
1 == "1"
true
===
równe co do wartości I typu
1 === "1"
false
!=
różne po konwersji
1 != "1"
false
!==
różne co do wartości albo typu
1 !== "1"
true
>
większe, napisy porównywane znak po znaku
"def" > "abc"
true
>=
większe albo równe
5 >= 5
true
== sprowadza obie strony do wspólnego typu i dopiero potem porównuje, dlatego napis "1" zrównał się z liczbą 1, a zero z wartością false. === zaczyna od typu: gdy typy są różne, od razu zwraca false. We własnym kodzie używa się ===, ale == trzeba rozpoznać, bo stoi w większości pytań. Napisy porównują się znak po znaku według kodów znaków, więc "def" > "abc" daje true, a "10" < "9" również, bo decyduje pierwszy znak.
Warunek złożony łączy kilka porównań operatorami logicznymi: && (I) wymaga obu części, || (LUB) wystarczy jedna, ! (NIE) odwraca wynik. Operator && wiąże mocniej niż ||, więc grupy zamyka się w nawiasach.
const kolor = "zielony";
const pojemnosc = 2;
const promocja = (kolor === "niebieski" || kolor === "zielony") && pojemnosc === 2;
console.log(promocja);
console.log(!promocja);
Warunek na przedział od 0 do 100 to a > 0 && a < 100. Ta sama para porównań z || jest prawdziwa dla każdej liczby, bo dowolna liczba jest albo większa od zera, albo mniejsza od stu. Instrukcje warunkowe i pętle opisuje osobna lekcja. Fałszem jest sześć wartości: false, 0, pusty napis, null, undefined i NaN. Wszystko inne jest prawdą, także napis "0" i liczba ujemna.
Operator trójargumentowy skraca wybór jednej z dwóch wartości do jednego wiersza: warunek, znak zapytania, wartość dla prawdy, dwukropek, wartość dla fałszu.
const stan = 0;
const opis = stan > 0 ? "dostępny" : "brak w magazynie";
console.log(opis);
Wynik: brak w magazynie.
Pułapka: = przypisuje, == porównuje wartości, === wartość razem z typem. Warunek if (x = 5) niczego nie porównuje: wpisuje do x piątkę i zawsze jest prawdziwy.
Łączenie napisów i szablony
Napisy skleja ten sam operator +, który dodaje liczby, więc "Cena: " + 120 daje Cena: 120, bo liczba zostaje zamieniona na napis. Szablon to napis w odwrotnych apostrofach, w którym wartość zmiennej wstawia się zapisem ${nazwa}: nie trzeba wtedy zamykać i otwierać cudzysłowu przy każdej zmiennej.
const nazwa = "Mysz";
const cena = 49;
console.log("Produkt: " + nazwa + ", cena " + cena + " zł");
console.log(`Produkt: ${nazwa}, cena ${cena} zł`);
console.log("Suma: " + (2 + 3));
console.log("Suma: " + 2 + 3);
Produkt: Mysz, cena 49 zł
Produkt: Mysz, cena 49 zł
Suma: 5
Suma: 23
Dwa ostatnie wiersze różni jeden nawias. Bez niego działania idą od lewej: do napisu dokleja się najpierw dwójka, potem trójka, więc zamiast sumy powstaje 23.
Na egzaminie
Pytania pisemne o zmienne i operatory mają kilka stałych kształtów. Ramka z kodem i pytanie, co wypisze skrypt albo jaką wartość ma zmienna na końcu: liczy się wiersz po wierszu, zapisując wynik każdego przypisania, a przy ++ pilnuje, czy znak stoi przed nazwą, czy po niej. Ten sam rachunek można przejść w przeglądarce w interpreterze JavaScript. Pytanie o typ: var x = true; daje typ logiczny, a typ zmiennej powstaje przez przypisanie wartości, nie przez deklarację. Pytanie o nazwę: poprawna jest imie2, odpadają nazwy z krzyżykiem, z polskim znakiem i ze znakiem procenta. Pytanie o grupę operatorów: && oraz || są logiczne, nie bitowe i nie przypisania. Pytanie o równoważność zapisów: zmienna++ znaczy to samo co zmienna += 1, a zmienna -= 1 to samo co zmienna--. Kilka odpowiedzi myli się regularnie. parseInt(3 / 2) daje 1, bo ucina część ułamkową, podczas gdy 3 / 2, Number(3 / 2) i parseFloat(3 / 2) dają 1.5. Wynikiem jest NaN wtedy, gdy działanie arytmetyczne dostało napis, a nie wtedy, gdy liczby były ujemne albo ułamkowe. Błąd logiczny w ramce, po którym skrypt nic nie wypisuje, to zwykle = zamiast == w instrukcji if. Operator % daje resztę, nie iloraz, więc parzystość sprawdza liczba % 2 === 0.
W zadaniu praktycznym zmienne trzymają wartość pobraną z pola formularza, wynik obliczenia i tekst komunikatu. Wartość z pola jest napisem, także z pola type="number", więc dodanie jej wprost sklei dwie liczby zamiast je zsumować: najpierw idzie Number albo parseInt, potem sprawdzenie isNaN, a na końcu rachunek i porównania z ===. Gotowy komunikat składa się szablonem i trafia do wskazanego elementu przez innerHTML, przy czym wstawianie tam tekstu wpisanego przez użytkownika bez sprawdzenia otwiera drogę do wykonania obcego kodu. Pobieranie elementów opisuje lekcja o modelu DOM, a odbiór danych z pól lekcja o zdarzeniach i formularzach. Ściąga
let licznik = 0;
zmienna o zasięgu bloku, wartość wolno zmieniać
const VAT = 0.23;
wartość nadana raz, ponowne przypisanie to błąd
var x = true;
starsze słowo, zasięg całej funkcji, typ logiczny
typeof 19.99
number, bo liczby są jednym typem
typeof null
object, mimo że null obiektem nie jest
"5" + 3
53, plus przy napisie skleja
"5" - 3
2, minus zamienia napis na liczbę
Number("12abc")
NaN, bo cały napis musi być liczbą
parseInt("12abc")
12, czyta do pierwszego niepasującego znaku
parseFloat("3.9 kg")
3.9, z częścią ułamkową
isNaN(x)
true, gdy wartości nie da się uznać za liczbę
7 % 3
1, reszta z dzielenia
2 ** 3
8, to samo co Math.pow(2, 3)
a++
oddaje starą wartość, potem zwiększa o jeden
1 == "1"
true, porównanie po konwersji
1 === "1"
false, bo typy są różne
Pobierz ściągę PDF