Rozdział 2. Podstawy JavaScriptu. Wartości, zmienne i kontrola sterowania

03 stycznia 2013
1 gwiadka2 gwiazdki3 gwiazdki4 gwiazdki5 gwiazdek

W świecie komputerów istnieją tylko dane. Jeśli coś nie jest informacją, to po prostu nie istnieje. Mimo że wszystkie dane to w istocie jedynie sekwencje bitów 1, a więc zasadniczo są tym samym, każda informacje odgrywa jakąś rolę. W systemie JavaScriptu większość danych jest zgrabnie wyodrębniona w postaci tzw. wartości. Każda wartość ma typ determinujący, jaką rolę ta wartość może pełnić. Wyróżnia się sześć podstawowych typów danych: liczby, łańcuchy, wartości logiczne, obiekty, funkcje oraz wartości niezdefiniowane.

Aby utworzyć wartość, wystarczy nadać jej nazwę. Jest to bardzo wygodne. Nie trzeba gromadzić materiałów do budowy wartości, ani za nie płacić. Wystarczy tylko nadać jej nazwę i abrakadabra, gotowe. Chociaż oczywiście wartości nie biorą się z niczego. Każda musi być gdzieś zapisana i jeśli utworzysz gigantyczną liczbę wartości na raz, to może zabraknąć Ci miejsca w pamięci komputera. Na szczęście problem ten dotyczy tylko sytuacji, w których wszystkie te wartości są potrzebne na raz. Gdy wartość przestaje być używana, zostaje usunięta i pozostaje po niej tylko kilka bitów. Bity te są odzyskiwane, aby można było ich użyć do przechowywania następnych wartości.


Wartości typu liczbowego to, jak się można spodziewać, liczby. Zapisuje się je tak, jak normalne liczby:

144

Jeśli wpiszesz tę liczbę w konsoli, w oknie wyjściowym pokaże się ta sama liczba. Wpisany przez Ciebie tekst spowodował utworzenie wartości liczbowej, a konsola wyświetliła ją na ekranie. Ten przykład niczego ciekawego nie pokazuje, ale wkrótce będziemy tworzyć wartości na inne sposoby i wówczas możliwość wypróbowania tego, co się utworzyło w konsoli będzie bardzo przydatna.

A tak wygląda liczba 144 w postaci bitowej2:

0100000001100010000000000000000000000000000000000000000000000000

Powyższa liczba składa się z 64 bitów. Z tylu bitów składają się wszystkie liczby w języku JavaScript, co ma jeden ważny skutek: ilość liczb, jakie można w ten sposób wyrazić jest ograniczona. Za pomocą trzech cyfr dziesiętnych można wyrazić tylko liczby od 0 do 999, czyli 103 = 1000 różnych liczb. Przy użyciu 64 cyfr binarnych można zapisać 264 różnych liczb. To bardzo dużo, więcej niż 1019 (jedynka z dziewiętnastoma zerami).

Jednak nie wszystkie liczby całkowite mniejsze od 1019 dadzą się wyrazić przy użyciu typów liczbowych języka JavaScript. Jednym z powodów jest to, że istnieją również liczby ujemne i jeden bit musi zostać użyty do przechowywania znaku liczby. Jeszcze większym problemem jest to, że reprezentowane muszą być liczby nie będące całkowitymi. Dlatego 11 bitów jest używanych do przechowywania informacji o położeniu kropki oddzielającej część całkowitą od ułamkowej.

W ten sposób pozostają 52 bity3. Każda liczba całkowita mniejsza od 252 (co jest więcej niż 1015) da się bez problemu wyrazić w JavaScripcie. Zazwyczaj używa się o wiele mniejszych liczb i opisane problemy w ogóle nas nie dotyczą. Bardzo dobrze. Nie mam nic przeciwko bitom, ale żeby cokolwiek zrobić, trzeba mieć ich ogromną ilość. Gdy jest to możliwe, przyjemniej jest posługiwać się większymi obiektami.

Liczby ułamkowe zapisuje się z użyciem kropki.

9.81

Do wyrażania bardzo dużych i bardzo małych liczb można też używać notacji naukowej. W tym celu należy dodać literę e i wykładnik potęgi:

2.998e8

Powyższy zapis jest równoważny z 2.998 * 108 = 299800000.

Działania wykonywane na liczbach całkowitych mieszczących się w granicy 52 bitów są zawsze dokładne. Niestety tego samego nie można powiedzieć o liczbach ułamkowych. Tak samo, jak nie można przy użyciu skończonej liczby cyfr dziesiętnych precyzyjnie określić wartości liczby π (pi), wielu liczb nie można precyzyjnie wyrazić przy użyciu tylko 64 bitów. Szkoda, ale tak naprawdę powoduje to realne problemy tylko w pewnych specyficznych sytuacjach. Ważne jest, aby o tym pamiętać i traktować ułamkowe liczby dziesiętne jako przybliżenia, a nie dokładne wartości.


Liczb najczęściej używa się do wykonywania działań arytmetycznych. Działania te, np. dodawanie czy mnożenie, polegają na pobraniu dwóch liczb i utworzeniu z nich nowej wartości. W języku JavaScript wygląda to tak:

100 + 4 * 11

Znaki + i * nazywają się operatorami. Pierwszy oznacza dodawanie, a drugi mnożenie. Umieszczenie operatora między dwiema wartościami powoduje zastosowanie go do tych wartości i utworzenie nowej wartości.

Czy ten przykład oznacza „dodaj 4 do 100, a następnie pomnóż otrzymany wynik przez 11”, czy może jednak mnożenie zostanie wykonane przed dodawaniem? Jak zapewne zgadłeś, najpierw wykonane zostanie mnożenie. Ale, podobnie jak w matematyce, można to zmienić za pomocą nawiasów:

(100 + 4) * 11

Do odejmowania służy operator -, a do dzielenia —/. Gdy w wyrażeniu znajduje się kilka operatorów i nie ma żadnych nawiasów, działania są wykonywane zgodnie z zasadami kolejności wykonywania działań. W pierwszym przykładzie udowodniłem, że mnożenie ma pierwszeństwo przed dodawaniem. Dzielenie i mnożenie są zawsze wykonywane przed odejmowaniem i dodawaniem. Jeśli obok siebie wystąpią operatory o takim samym priorytecie (1 - 1 + 1), to są wykonywane po kolei od lewej.

Spróbuj obliczyć wynik poniższego wyrażenia, a potem wykonaj program, aby sprawdzić, czy dobrze policzyłeś…

115 * 4 - 4 + 88 / 2

Nie musisz jednak zbytnio przejmować się zasadami kolejności wykonywania działań, ponieważ w razie wątpliwości zawsze możesz użyć nawiasów.

Istnieje jeszcze jeden operator arytmetyczny, którego możesz nie znać. Oznacza się go symbolem % i reprezentuje on działanie zwracania reszty z dzielenia. Wynikiem działania X % Y będzie reszta z podzielenia X przez Y. Na przykład 314 % 100 wynosi 14, 10 % 3 wynosi 1, a 144 % 12 równa się 0. Reszta z dzielenia ma taki sam priorytet, jak mnożenie i dzielenie.


Drugi typ danych to łańcuch. Jego przeznaczenie nie jest w tak oczywisty sposób związane z nazwą, jak w przypadku liczb, ale ten typ również jest bardzo potrzebny. Łańcuchy służą do reprezentowania tekstu, a ich nazwa zapewne wzięła się stąd, że wartości tego typu są po prostu ciągami znaków. Łańcuchy zapisuje się cudzysłowach prostych:

"Załataj moją łódkę gumą do żucia."

W podwójnym cudzysłowie można wpisać prawie wszystko, a JavaScript zrobi z tego wartość łańcuchową. Jest jednak kilka znaków, na które trzeba uważać. Po pierwsze trudności w cudzysłowie sprawiają właśnie cudzysłowy. Po drugie problemy możesz mieć ze wstawianiem znaków nowego wiersza, czyli tych znaków, które są wstawiane, gdy użytkownik naciśnie klawisz Enter. Ich również nie można umieszczać w cudzysłowach, ponieważ każdy łańcuch musi w całości znajdować się w jednym wierszu.

Do wstawiania tych znaków do łańcuchów używa się sztuczki. Ukośnik () w cudzysłowie oznacza, że znak znajdujący się za nim ma specjalne znaczenie. Jeśli za ukośnikiem znajduje się cudzysłów prosty, to cudzysłów ten nie spowoduje zakończenia łańcucha, ale stanie się jego częścią. Natomiast znak n za ukośnikiem oznacza nowy wiersz. Analogicznie t za ukośnikiem oznacza tabulator4.

"To jest pierwszy wiersz\nA to jest drugi wiersz"

Jeśli wpiszesz ten tekst w konsoli, to zostanie on wyświetlony w oknie wyjściowym w niezmienionej formie, tzn. z cudzysłowami i ukośnikami. Aby zobaczyć sam wynik, możesz wpisać np. print("anb"). Działanie tej instrukcji zostanie szczegółowo objaśnione później.

Oczywiście zdarza się też, że w tekście ma znajdować się sam ukośnik jako jeden ze zwykłych znaków. Aby umieścić ukośnik w łańcuchu, należy wpisać dwa ukośniki bezpośrednio obok siebie:

"Znak nowego wiersza zapisuje się tak: "\n"."

Łańcuchów nie można dzielić, mnożyć ani odejmować. Można natomiast używać z nimi operatora +. Jednak w przypadku łańcuchów operator ten nie dodaje tylko wykonuje konkatenację, czyli po prostu łączenie.

"kon" + "kat" + "e" + "nacja"

Na łańcuchach można wykonywać także inne działania, ale wrócimy do tego później.


Nie wszystkie operatory mają postać symboliczną. Niektóre występują jako słowa. Na przykład operator typeof tworzy wartość łańcuchową reprezentującą typ podanej mu wartości.

typeof 4.5

Operatory, które poznałeś wcześniej działały na dwóch argumentach, natomiast typeof pobiera tylko jeden. Operatory wykonujące działania na dwóch argumentach nazywają się binarnymi lub dwuargumentowymi, natomiast działające na pojedynczych wartościach to operatory jednoargumentowe. Operator minus może być używany zarówno jako dwu- jak i jednoargumentowy:

- (10 - 2)

Kolejnym typem wartości są wartości logiczne. Są tylko dwie takie wartości: true i false. Oto jeden ze sposobów na uzyskanie wartości true:

3 > 2

Natomiast false można otrzymać tak:

3 < 2

Mam nadzieję, że wiesz, co oznaczają znaki > i <. Są to odpowiednio znak większości i mniejszości. Są to operatory dwuargumentowe, a wynikiem ich działania jest wartość logiczna oznaczająca, czy dane wyrażenie jest prawdziwe, czy fałszywe.

W ten sam sposób można porównywać łańcuchy:

"Aardvark" < "Zoroaster"

Łańcuchy są porządkowane mniej więcej alfabetycznie. Mniej więcej… Wielkie litery są zawsze „mniejsze” od małych, a więc wartością wyrażenia "Z" < "a" (wielka litera Z i mała litera a) jest true. Znaki nie należące do alfabetu (!, @, itd.) również są uwzględniane w porządkowaniu. W rzeczywistości porównywanie znaków odbywa się na podstawie standardu Unicode. W standardzie tym każdemu znakowi, jaki sobie można wyobrazić, od znaków greckiego alfabetu przez arabski, japoński i tamilski aż po polski, przypisany jest numer. Numery te ułatwiają przechowywanie łańcuchów w komputerze, ponieważ można je zapisywać jako listy liczb. Podczas porównywania łańcuchów JavaScript porównuje po prostu numery znaków w tych łańcuchach od lewej.

Inne operatory tego typu to >= (większy lub równy), <= (mniejszy lub równy), == (równy) oraz != (różny).

"Szast" != "Prast"
5e2 == 500

Na wartościach logicznych można też wykonywać pewne działania, które są bardzo przydatne. JavaScript obsługuje trzy operatory logiczne: i, lub oraz nie. Można ich używać do „rozumowania” o wartościach logicznych.

Logiczne and jest reprezentowane przez operator &&. Jest to operator logiczny, który zwraca wartość true tylko wtedy, gdy oba jego argumenty mają wartość true.

true && false

Operator || to logiczne lub. Zwraca wartość true, gdy którykolwiek z jego argumentów ma wartość true:

true || false

Operator logicznego nie ma postać wykrzyknika (!). Jest to operator jednoargumentowy zamieniający podaną mu wartość na przeciwną, a więc !true oznacza false, a !false oznacza true.


Ćw. 2.1
((4 >= 6) || ("zielona" != "trawa")) &&

   !(((12 * 2) == 144) && true)

Prawda czy fałsz? W kodzie tym znajduje się sporo nawiasów, które ułatwiają jego zrozumienie, ale nie są niezbędne. Można go zapisać prościej w taki sposób:

(4 >= 6 || "zielona" != "trawa") &&

   !(12 * 2 == 144 && true)

Nie zawsze jest oczywiste, czy nawiasy są potrzebne. Zazwyczaj wystarczy tylko pamiętać, że z operatorów poznanych do tej pory najniższy priorytet ma ||, następny jest &&, później są operatory porównawcze (>, == itd.), a potem reszta. Priorytety operatorów zostały tak dobrane, że w prostych przypadkach można obejść się z minimalną ilością nawiasów.


We wszystkich przedstawionych do tej pory przykładach język JavaScript był używany w taki sam sposób, jak używa się kalkulatora kieszonkowego. Po prostu tworzone były określone wartości, na których wykonywano działania przy użyciu operatorów. Tworzenie wartości w ten sposób jest ważną częścią każdego programu JavaScript, ale nie jedyną. Kod zwracający jakąś wartość nazywa się wyrażeniem. Wyrażeniem jest każda wartość zapisana bezpośrednio w kodzie (np. 22 albo "psychoanaliza"). To, co znajduje się w nawiasie również jest wyrażeniem. Jest nim także operator dwuargumentowy zastosowany do dwóch wyrażeń jak i operator jednoargumentowy zastosowany do jednego wyrażenia.

Są jeszcze inne sposoby tworzenia wyrażeń, ale poznasz je w stosownym czasie.

Istnieje też jednostka programowa o szerszym zakresie niż wyrażenie. Jest to instrukcja. Program jest zestawem instrukcji. Większość z nich jest zakończona średnikiem (;). Najprostsza instrukcja to wyrażenie zakończone średnikiem. To jest program:

1;
!false;

Program ten jest bezużyteczny. Wyrażenie może być jedynie treścią stanowiącą jakąś wartość, natomiast instrukcja ma sens tylko wtedy, gdy coś zmienia. Może np. drukować coś na ekranie — to liczy się jako zmiana czegoś w otaczającym świecie — albo zmieniać wewnętrzny stan programu, co spowoduje zmianę działania dalszych instrukcji. Zmiany te nazywają się „skutkami ubocznymi”. Instrukcje w powyższym przykładzie tworzą tylko wartości 1 i true, a następnie wrzucają je do wora z nieużywanymi bitami5. Nie ma to żadnego wpływu na otaczający je świat i nie wywołuje żadnych skutków ubocznych.


W jaki sposób program utrzymuje swój stan wewnętrzny? Jak to się dzieje, że różne rzeczy są przez niego pamiętane? Widzieliśmy już przykłady tworzenia nowych wartości z istniejących wartości. Te operacje nie powodowały zmiany tych starych wartości, a nowa wartość musi zostać od razu użyta, jeśli nie chcemy, aby zniknęła. Dlatego do przechowywania wartości w języku JavaScript używa się zmiennych.

var iloczyn = 5 * 5;

Zmienna musi mieć nazwę i może wskazywać jakąś wartość. Powyższa instrukcja tworzy zmienną o nazwie iloczyn i zapisuje w niej wynik mnożenia 5 razy 5.

Gdy uruchomisz ten program, możesz wpisać w konsoli słowo iloczyn, aby wyświetlić wartość 25. Nazwa zmiennej służy do pobierania reprezentowanej przez nią wartości. Można też napisać iloczyn + 1. Nazw zmiennych można używać jako wyrażeń, a więc mogą one wchodzić w skład także większych wyrażeń.

Do tworzenia zmiennych służy słowo kluczowe var. Po nim należy wpisać nazwę zmiennej. Jako nazwy zmiennej można użyć prawie każdego słowa. Nie można natomiast w nazwach zmiennych używać spacji. Także cyfry są dozwolone, np. iloczyn22, ale nie mogą znajdować się na początku. Znaki $ i _ również mogą występować w nazwach zmiennych i mają w nich taki sam status, jak litery, a więc nazwa $_$ jest poprawna.

Jeśli chcesz utworzonej zmiennej od razu przypisać wartość, co często się robi, możesz użyć operatora =, aby przypisać jej wartość jakiegoś wyrażenia.

Przypisanie zmiennej wartości nie oznacza, że musi tak pozostać na zawsze. Wartość istniejącej zmiennej można w dowolnym momencie zmienić za pomocą operatora =.

iloczyn = 4 * 4;

Zmienne najlepiej jest wyobrażać sobie jako przyssawki, nie pudełka. Jest tak dlatego, ponieważ wartości nie są w nich przechowywane, a jedynie zmienne do tych wartości się odwołują ― nie ma przeszkód, aby dwie różne zmienne odwoływały się do tej samej wartości. Program ma dostęp tylko do tych wartości, do których są jakieś odwołania. Gdy trzeba coś zapamiętać, przyczepia się do tego nową przyssawkę albo odczepia się istniejącą od czegoś innego i przyczepia w nowym miejscu: aby zapamiętać, ile pieniędzy winny jest Ci Lutek, możesz napisać…

var dlugLutka = 140;

Gdy Lutek zwróci część tej kwoty, wartość tę można zmniejszyć przypisując zmiennej nową liczbę:

dlugLutka = dlugLutka - 35;

Zbiór zmiennych i ich wartości w określonym czasie nazywa się środowiskiem. Nie jest ono puste podczas uruchamiania programu. Zawsze znajduje się w nim kilka standardowych zmiennych. Gdy przeglądarka wczytuje stronę, tworzy nowe środowisko i dodaje do niego te standardowe zmienne. Zmienne tworzone i modyfikowane przez programy na tej stronie istnieją dopóki nie zostanie otwarta nowa strona.


Wiele wartości dostępnych w standardowym środowisku jest typu funkcyjnego. Funkcja to fragment programu zapakowanego w wartości. Zazwyczaj fragment ten robi coś pożytecznego i można go wywołać za pomocą zawierającej go wartości. W środowisku przeglądarki istnieje zmienna alert przechowująca funkcję wyświetlającą małe okienko dialogowe z komunikatem. Używa się jej następująco:

alert("Awokado");

Wykonanie kodu funkcji nazywa się wywołaniem funkcji. Do tego celu używa się nawiasu. Wywołać przy użyciu nawiasów można każde wyrażenie tworzące wartość funkcyjną. W powyższym przykładzie funkcji przekazano wartość "Awokado", która została wyświetlona jako napis w oknie dialogowym. Wartości przekazywane do funkcji nazywają się parametrami lub argumentami. Funkcja alert wymaga tylko jednego argumentu, ale są funkcje, którym trzeba podać więcej parametrów.


Wyświetlenie okna dialogowego jest efektem ubocznym. Wiele funkcji jest przydatnych właśnie ze względu na ich efekty uboczne. Funkcja może też tworzyć wartość i wtedy jest przydatna mimo że nie ma skutku ubocznego. Istnieje np. funkcja o nazwie Math.max, która przyjmuje dowolną liczbę wartości liczbowych i zwraca największą z nich:

alert(Math.max(2, 4));

Tworzenie wartości przez funkcję nazywa się zwracaniem wartości. Ponieważ w języku JavaScript wartości są zawsze tworzone przez wyrażenia, wywołań funkcji można używać jako składników większych wyrażeń:

alert(Math.min(2, 4) + 100);

Techniki tworzenia własnych funkcji są opisane w rozdziale 3.


Jak pokazałem w poprzednich przykładach, za pomocą funkcji alert można wyświetlić wynik jakiegoś wyrażenia. Chociaż te okienka, które trzeba zamykać kliknięciem niejednego już doprowadziły do szewskiej pasji. Dlatego od tej pory będziemy używać podobnej funkcji, o nazwie print, która nie wyświetla okna dialogowego, tylko drukuje wartość w polu wyjściowym konsoli. Funkcja print nie jest standardową funkcją JavaScript i nie jest normalnie obsługiwana przez przeglądarki, ale można jej używać na stronach tego podręcznika.

print("N");

Podobną funkcją również dostępną tylko na tych stronach jest show. Funkcja print drukuje swój argument jako tekst, natomiast show próbuje zaprezentować go w taki sposób, jak wyglądałby w programie, dzięki czemu otrzymujemy więcej informacji na temat typu wartości. Na przykład wartości łańcuchowe przez funkcję show są wyświetlane wraz z cudzysłowami:

show("N");

W standardowym środowisku przeglądarek dostępnych jest jeszcze kilka innych funkcji wyświetlających wyskakujące okienka. Np. za pomocą funkcji confirm można wyświetlić okno, w którym użytkownik musi kliknąć przycisk OK lub Anuluj jako odpowiedź na pytanie. Jeśli użytkownik kliknie przycisk OK, funkcja zwraca wartość true, a jeśli użytkownik kliknie przycisk Anuluj, funkcja zwraca wartość false.

show(confirm("Możemy?"));

Funkcji prompt można użyć, aby zadać pytanie otwarte. Pierwszy argument zawiera pytanie, a drugi tekst, który zostanie wstępnie wyświetlony w polu tekstowym na odpowiedź. Gdy użytkownik wpisze jakiś tekst w oknie, funkcja zwróci go jako łańcuch.

show(prompt("Powiedz nam wszystko, co wiesz.", "..."));

Prawie każdej zmiennej w środowisku można nadać nową wartość. Bywa to przydatne, ale i niebezpieczne. Jeśli funkcji print przypisze się wartość 8, to nie będzie można już nic przy jej użyciu wydrukować. Na szczęście w konsoli znajduje się duży przycisk Reset przywracający środowisko do pierwotnego stanu.


Liniowe programy nie są zbyt interesujące. Gdy program zawiera więcej niż jedną instrukcję, instrukcje te, jak nietrudno się domyślić, są wykonywane po kolei zaczynając od góry.

var liczba = Number(prompt("Wybierz liczbę", ""));
print("Twoja liczba jest kwadratowym pierwiastkiem liczby " +
      (liczba * liczba));

Funkcja Number konwertuje wartość na liczbę, co w tym przypadku było konieczne, ponieważ funkcja prompt zwraca łańcuch. Istnieją też funkcje o nazwach String i Boolean zamieniające wartości odpowiednio na łańcuchy i typ logiczny.


Spróbujmy napisać program drukujący wszystkie liczby parzyste z przedziału od 0 do 12. Oto jedna z możliwości:

print(0);
print(2);
print(4);
print(6);
print(8);
print(10);
print(12);

To działa, ale programy pisze się po to, aby zmniejszyć ilość pracy, a nie sobie jej dodać. Gdybyśmy chcieli wyświetlić wszystkie parzyste liczby z przedziału do 100, to powyższa metoda byłaby niesamowicie pracochłonna. Dlatego potrzebujemy jakiegoś sposobu na wielokrotne wykonywanie kodu.

var biezacaLiczba = 0;
while (biezacaLiczba <= 12) {
  print(biezacaLiczba);
  biezacaLiczba = biezacaLiczba + 2;
}

Instrukcję while mogłeś już zauważyć w rozdziale wstępnym. Instrukcja zaczynająca się od słowa while stanowi pętlę. Pętla zaburza normalny tok wykonywania instrukcji, ponieważ może zmusić program do wykonania określonego zestawu instrukcji wielokrotnie. W tym przypadku za słowem while znajduje się wyrażenie w nawiasie (nawias ten jest tu obowiązkowy), które decyduje o tym, czy działanie pętli ma zostać zakończone, czy być kontynuowane. Dopóki wartością logiczną tego wyrażenia jest true, pętla powtarza wykonywanie zawartego w niej kodu. Gdy wartość wyrażenia logicznego zmieni się na false, następuje przejście na dół pętli i wykonywana jest dalsza część programu w normalny sposób.

Zmienna biezacaLiczba demonstruje, w jaki sposób za pomocą zmiennej można śledzić postęp wykonywania programu. W każdym powtórzeniu pętli wartość tej zmiennej jest zwiększana o 2 i na początku każdego powtórzenia wartość ta jest porównywana z liczbą 12, w celu określenia, czy należy wykonać następne powtórzenie, czy nie.

Trzecią częścią instrukcji while jest inna instrukcja. Stanowi ona treść właściwą pętli, czyli działanie lub działania, które mają zostać wykonane wielokrotnie. Gdybyśmy nie musieli drukować liczb, nasz program mógłby wyglądać tak:

var biezacaLiczba = 0;
while (biezacaLiczba <= 12)
  biezacaLiczba = biezacaLiczba + 2;

W tym kodzie zasadniczą treść pętli stanowi instrukcja biezacaLiczba = biezacaLiczba + 2;. My musimy jednak wydrukować nasze liczby, a więc musimy użyć więcej niż jednej instrukcji. Do grupowania instrukcji w bloki służą klamry ({ i }). Dla świata zewnętrznego blok jest pojedynczą instrukcją. We wcześniejszym przykładzie w bloku znajdują się wywołanie funkcji print i instrukcja zmieniająca wartość zmiennej biezacaLiczba.


Ćw. 2.2

Korzystając z poznanych technik napisz program obliczający i wyświetlający wynik działania 210 (2 do potęgi 10). Oczywiście nie możesz stosować prymitywnych sztuczek w stylu 2 * 2 *...

Jeśli masz problem z rozwiązaniem tego zadania, dokładnie przyjrzyj się przykładowi drukującemu liczby parzyste. Ten program musi wykonać pewne działanie określoną liczbę razy. Do jego budowy można użyć zmiennej licznikowej i pętli while. Zamiast drukować licznik program musi pomnożyć coś przez 2. Tym czymś powinna być inna zmienna, w której stopniowo gromadzony jest wynik.

Nie martw się, jeśli jeszcze to do Ciebie nie przemawia. Nawet gdy doskonale zrozumiesz wszystkie opisywane w tym zagadnienia, możesz mieć trudności z ich praktycznym wykorzystaniem. Umiejętność posługiwania się kodem przychodzi wraz z praktyką i dlatego przeanalizuj rozwiązanie, a potem spróbuj wykonać następne ćwiczenie.


Ćw. 2.3

Wprowadzając drobne modyfikacje program napisany w poprzednim ćwiczeniu można wykorzystać do narysowania trójkąta. Pod pojęciem „narysować trójkąt” rozumiem oczywiście wydrukowanie tekstu w taki sposób, aby przypominał tę figurę geometryczną.

Wydrukujemy dziesięć linijek tekstu. W pierwszej będzie znajdować się tylko jeden znak #. W drugiej linijce będą dwa takie znaki itd.

Jak sprawić, aby w linii zostało wydrukowanych X znaków #? Jednym ze sposobów jest utworzenie w każdym powtórzeniu linii za pomocą wewnętrznej pętli — tzn. pętli znajdującej się w innej pętli. Jednak prościej będzie utworzyć łańcuch i w każdej iteracji dodawać do niego po jednym znaku.


Zwróć uwagę na odstępy znajdujące się przed niektórymi instrukcjami. Nie są one wymagane, tzn. dla komputera nie ma różnicy czy one są, czy ich nie ma. W istocie nawet złamania wierszy w programach nie są obowiązkowe. Jeśli chcesz, cały program możesz napisać w jednym wierszu. Wcięcia w blokach kodu mają za zadanie ukazać strukturę programu programiście czytającemu kod. Ponieważ w istniejących blokach można tworzyć kolejne bloki, czasami może być trudno zorientować się, gdzie każdy blok się rozpoczyna i kończy, zwłaszcza w skomplikowanych programach. Gdy zastosowane są wcięcia, program wizualnie odzwierciedla strukturę bloków, z których jest zbudowany. Ja dla każdego nowego bloku stosuję wcięcie na głębokość dwóch spacji, ale są różne gusta.

Pole w konsoli do wprowadzania kodu programów dodaje odstępy automatycznie. Początkowo może Cię to denerwować, ale gdy napiszesz większą ilość kodu, docenisz jak dużo czasu można dzięki temu zaoszczędzić. Naciskając klawisz Tab można zwiększyć wcięcie wiersza, w którym znajduje się kursor.

W pewnych szczególnych przypadkach w języku JavaScript dopuszczalne jest opuszczenie średnika na końcu instrukcji. W innych sytuacjach średnik koniecznie musi być użyty albo dzieją się dziwne rzeczy. Reguły dotyczące tego, kiedy można opuścić średnik są zawiłe i dziwaczne. W tym podręczniku nie opuściłem ani jednego średnika i zachęcam Cię, abyś robił tak samo.


Wszystkie przedstawione do tej pory przykłady użycia instrukcji while są zbudowane wg jednej zasady. Najpierw tworzona jest zmienna reprezentująca licznik. Za jej pomocą śledzony jest postęp wykonywania programu. Instrukcja while sama zawiera test, który najczęściej polega na sprawdzeniu, czy licznik osiągnął określoną wartość. Następnie na końcu właściwej treści pętli następuje zwiększenie wartości licznika.

W ten sposób można napisać wiele różnych pętli. Dlatego w języku JavaScript, i wielu innych językach, dostępna jest nieco bardziej zwięzła postać pętli:

for (var liczba = 0; liczba <= 12; liczba = liczba + 2)
  show(liczba);

Ten program zwróci dokładnie taki sam wynik, jak pokazany wcześniej program drukujący liczby parzyste. Jedyna różnica między tymi dwiema pętlami polega na tym, że w tym przypadku wszystkie instrukcje dotyczące „stanu” pętli znajdują się w jednym wierszu. W nawiasie znajdującym się za słowem for powinny znajdować się dwa średniki. Kod znajdujący się przed pierwszym średnikiem inicjuje pętlę i zazwyczaj zawiera definicję zmiennej. Druga część jest wyrażeniem sprawdzającym, czy wykonywanie pętli powinno być kontynuowane. Ostatnia część aktualizuje stan pętli. W większości przypadków ten rodzaj pętli jest bardziej zwięzły i prostszy od konstrukcji while.


W nazwach niektórych zmiennych można zauważyć dziwny sposób użycia wielkich liter. Przypomnę, że nazwy zmiennych nie mogą zawierać spacji, ponieważ komputer traktowałby je jako kilka oddzielnych słów, i dlatego nazwy składające się z kilku słów trzeba zapisywać na różne inne sposoby: malyfiglarnykonik, maly_figlarny_konik, MałyFiglarnyKonik lub malyFiglarnyKonik. Pierwsza wersja jest trudna do odczytania. Podoba mi się wersja ze znakami podkreślenia, ale ich wpisywanie jest kłopotliwe. Nazwy standardowych funkcji języka JavaScript są pisane przy użyciu ostatniej metody, z której korzysta też wielu programistów. Nietrudno jest się do tego przyzwyczaić i dlatego będę postępował jak inni, czyli będą zapisywał z wielkiej litery wszystkie słowa w nazwach oprócz pierwszego.

Istnieje też kilka przypadków, jak nazwa funkcji Number, w których wielka jest też litera pierwszego słowa. Umownie oznacza to, że funkcja ta jest konstruktorem. Czym są konstruktory dowiesz się w rozdziale 8. Na razie po prostu ignoruj ten pozorny brak konsekwencji.

Pamiętaj też, że niektóre słowa, np. var, while i for, mają w języku specjalne znaczenie i nie można ich używać do nazywania zmiennych. Są to tzw. słowa kluczowe. Istnieje też grupa słów zarezerwowanych do użytku przez JavaScript w przyszłości. Oficjalnie ich również nie można używać jako nazw zmiennych, chociaż niektóre przeglądarki tego nie przestrzegają. Oto lista wszystkich słów tego rodzaju:

abstract boolean break byte case catch char class const continue
debugger default delete do double else enum export extends false
final finally float for function goto if implements import in
instanceof int interface long native new null package private
protected public return short static super switch synchronized
this throw throws transient true try typeof var void volatile
while with

Nie męcz się, aby je wszystkie od razu zapamiętać. Wystarczy że sobie o nich przypomnisz, gdy Twój program nie będzie działał zgodnie z Twoimi oczekiwaniami. Z mojego doświadczenia wynika, że najczęściej używanymi niedozwolonymi nazwami są słowa char (do przechowywania pojedynczego znaku) i class.


Ćw. 2.4

Przepisz poprzednie przykłady użycia pętli while przy użyciu pętli for.


W programach często zmienia się wartości zmiennych na wartości będące modyfikacją ich poprzednich wartości. Na przykład licznik = licznik + 1. W języku JavaScript istnieje możliwość wyrażenia tego w skrócony sposób: licznik += 1. Technikę te można stosować także z wieloma innymi operatorami, np. wynik *= 2 podwaja wartość zmiennej wynik, a licznik -= 1 zmniejsza wartość licznika.

Konstrukcje licznik += 1 i licznik -= 1 można nawet zapisać jeszcze krócej: licznik++ i licznik--.


Mówi się, że pętle zmieniają przepływ sterowania w programie. Zmieniają kolejność wykonywania instrukcji. W wielu przypadkach potrzebna jest też zmiana przepływu w inny sposób: poprzez pomijanie instrukcji.

Wyobraź sobie, że chcesz wyświetlić wszystkie liczby mniejsze od 20, które są podzielne zarówno przez 3, jak i 4.

for (var licznik = 0; licznik < 20; licznik++) {
  if (licznik % 3 == 0 && licznik % 4 == 0)
    show(licznik);
}

Słowo kluczowe if działa bardzo podobnie do słowa while: sprawdza warunek podany w nawiasie i podejmuje decyzję czy wykonać instrukcję w zależności od wartości tego warunku. Robi to jednak tylko raz, a więc instrukcja może zostać wykonana zero razy lub jeden raz.

Sztuczka z operatorem reszty z dzielenia (%) pozwala w łatwy sposób sprawdzić czy jedna liczba dzieli się bez reszty przez inną. Jeśli tak, to reszta z dzielenia zwrócona przez ten operator wynosi zero.

Gdybyśmy chcieli wyświetlić wszystkie liczby z przedziału do 20, a podzielne przez 4 umieścić w nawiasach, moglibyśmy napisać taki kod:

for (var licznik = 0; licznik < 20; licznik++) {
  if (licznik % 4 == 0)
    print(licznik);
  if (licznik % 4 != 0)
    print("(" + licznik + ")");
}

Teraz jednak program musi sprawdzić czy wartość zmiennej licznik jest podzielna przez 4 dwa razy. Ten sam efekt można uzyskać dodając instrukcję else za instrukcją if. Instrukcja elsejest wykonywana tylko wtedy, gdy wartość warunku instrukcji if jest fałszywa.

for (var licznik = 0; licznik < 20; licznik++) {
  if (licznik % 4 == 0)
    print(licznik);
  else
    print("(" + licznik + ")");
}

Ten przykład jest banalny, ale możemy go trochę skomplikować. Spróbujemy wydrukować te same liczby, ale po każdej większej od 15 dodamy dwie gwiazdki, po każdej większej od 10 i mniejszej od 15 dodamy po jednej gwiazdce, a do pozostałych nic nie będziemy dodawać.

for (var licznik = 0; licznik < 20; licznik++) {
  if (licznik > 15)
    print(licznik + "**");
  else if (licznik > 10)
    print(licznik + "*");
  else
    print(licznik);
}

Jak widać w powyższym kodzie, instrukcje if można łączyć w łańcuchy. ten program najpierw sprawdza czy wartość zmiennej licznik jest większa od 15. Jeśli tak, następuje wydruk dwóch gwiazdek, a pozostałe testy są pomijane. Jeśli nie, program sprawdza czy wartość zmiennej licznik jest większa od 10. Jeśli wartość zmiennej licznik nie jest większa od 10, następuje wykonanie ostatniej instrukcji print.


Ćw. 2.5

Napisz program pytający za pomocą instrukcji prompt, ile wynosi 2 + 2. Jeśli zostanie udzielona odpowiedź 4, wyświetl jakąś pochwalę przy użyciu instrukcji alert. Jeśli zostanie wpisana liczba 3 albo 5, wyświetl napis „Prawie!”. W pozostałych przypadkach wydrukuj coś nieprzyjemnego.


Jeśli nie ma potrzeby, aby wszystkie instrukcje pętli były wykonywane do samego końca, można posłużyć się słowem kluczowym break. Powoduje ono natychmiastowe wyjście z bieżącej pętli i kontynuowanie wykonywania programu za nią. Ten program znajduje pierwszą liczbę większą od 20 i podzielną przez 7:

for (var biezaca = 20; ; biezaca++) {
  if (biezaca % 7 == 0)
    break;
}
print(biezaca);

W powyższej instrukcji for brakuje warunku pozwalającego zakończyć jej działanie. Z tego powodu zakończenie wykonywania tej pętli może nastąpić wyłącznie dzięki instrukcji break. Ten sam program można by było również zapisać tak:

for (var biezaca = 20; biezaca % 7 != 0; biezaca++)
  ;
print(biezaca);

W tym przypadku treść pętli jest pusta. Za pomocą samego średnika można utworzyć pustą instrukcję. Jedynym skutkiem działania tej pętli jest zwiększenie wartości zmiennej biezaca do żądanego poziomu. Potrzebny był mi jednak też przykład z użyciem instrukcji break, a więc dobrze przyjrzyj się też poprzedniej wersji.


Ćw. 2.6

Do rozwiązania poprzedniego ćwiczenia dodaj instrukcję while i ewentualnie break, aby pytanie było wyświetlane, aż w końcu zostanie podana prawidłowa odpowiedź.

Pętlę, która nigdy nie zakończy działania sama można utworzyć pisząc while (true) .... Może się to wydawać trochę głupie, że nakazujemy programowi wykonywać instrukcje dopóki true jest true, ale to naprawdę bardzo przydatna sztuczka.


W rozwiązaniu poprzedniego ćwiczenia znalazła się instrukcja var odpowiedz;. Tworzy ona zmienną o nazwie odpowiedz, ale nie nadaje jej żadnej wartości. Co się stanie, gdy spróbujemy użyć wartości tej zmiennej?

var tajemniczaZmienna;
show(tajemniczaZmienna);

Posługując się metaforą przyssawek, można powiedzieć, że ta zmienna nie jest do niczego przyssana. Gdy spróbujesz użyć wartości pustego miejsca, otrzymasz specjalną wartość o nazwie undefined. Także funkcje nie zwracające żadnej sensownej wartości, takie jak np. print i alert, zwracają wartość undefined.

show(alert("Jestem skutkiem ubocznym."));

Istnieje też podobna wartość null, która oznacza, że „dana zmienna jest zdefiniowana, ale nie ma żadnej wartości”. Różnica znaczenia między wartościami undefined i null ma przede wszystkim znaczenie naukowe i nie jest zbyt ciekawa. W praktyce zwykle sprawdza się, czy coś „ma wartość”. W takich przypadkach można używać wyrażeń typu cokolwiek == undefined, ponieważ, mimo że wartości te nie są tym samym, wyrażenie null == undefined ma wartość true.


To prowadzi nas do kolejnego ciekawego tematu…

show(false == 0);
show("" == 0);
show("5" == 5);

Wszystkie te instrukcje zwracają wartość true. Do porównywania wartości różnych typów JavaScript stosuje skomplikowane i niejasne reguły. Nie będę nawet próbował ich dokładnie wyjaśnić, ale najprościej można powiedzieć, że zazwyczaj próbuje przekonwertować jedną z wartości na typ drugiej. Jeśli jednak z którejś strony wystąpi wartość null lub undefined, wartość true jest zwracana tylko wtedy, gdy z obu stron znajduje się null lub undefined.

Jak w takim razie sprawdzić, czy wybrana zmienna odnosi się do wartości false? Zgodnie z regułami konwersji łańcuchów i liczb na wartości logiczne 0 i pusty łańcuch są traktowane jako false, natomiast wszystkie pozostałe wartości oznaczają true. Z tego względu wartością wyrażenia zmienna == false jest true, gdy zmienna odwołuje się do 0 lub "". Jeśli nie chcesz, aby ta automatyczna konwersja typów była wykonywana, możesz użyć jednego z dwóch dodatkowych operatorów: === i !==. Pierwszy sprawdza czy dana wartość jest identyczna z drugą, natomiast drugi — czy dana wartość jest dokładnie różna od drugiej.

show(null === undefined);
show(false === 0);
show("" === 0);
show("5" === 5);

Wszystkie te instrukcje zwrócą wartość false.


Wartości używane w warunkach w instrukcjach if, while oraz for nie muszą być logiczne. Przed sprawdzeniem zostaną automatycznie przekonwertowane na ten typ. Oznacza to, że liczba 0, pusty łańcuch "", null, undefined i oczywiście false zostaną potraktowane jako fałsz.

Wykorzystując fakt, że wszystkie pozostałe wartości zostaną zamienione na wartość true w wielu przypadkach można opuścić bezpośrednie porównywanie. Jeśli wiadomo, że zmienna może zawierać tylko albo łańcuch albo wartość null, można ją bardzo łatwo testować…

var mozeNull = null;
// …tajemniczy kod, który może zapisać łańcuch w zmiennej mozeNull…
if (mozeNull)
  print("Zmienna mozeNull ma wartość");

Wyjątkiem jest sytuacja, w której tajemniczy kod przypisze zmiennej mozeNull wartość "". Pusty łańcuch jest równoznaczny z fałszem, a więc w tym przypadku nic nie zostanie wydrukowane. W niektórych przypadkach może to być błąd. Tego subtelnego błędu można uniknąć używając porównania === null lub === false. To samo dotyczy wartości liczbowych, które mogą wynosić 0.


Wiersz zawierający opis tajemniczego kodu w powyższym przykładzie może wydać Ci się trochę podejrzany. Dodatkowe objaśnienia w kodzie programów są często bardzo przydatne. Zazwyczaj stosuje się je, aby wyjaśnić ludzkim językiem, co robi dany fragment programu.

// Licznik zmiennej, którego definicja znajduje się poniżej, będzie miał
// wartość początkową 0, czyli zero.
var licznik = 0;
// Teraz rozpoczynamy pętlę, więc dobrze się trzymaj.
while (licznik < 100 /* Licznik ma wartość mniejszą od stu */)
/* W każdym powtórzeniu pętli ZWIĘKSZAMY wartość licznika
   Dodajemy do niej jeden */
  licznik++;
// Koniec.

Tego rodzaju teksty nazywają się komentarzami. Oto reguły ich stosowania: ‚ciąg /* stanowi początek komentarza, który kończy się ciągiem */. ‚Ciąg // również oznacza początek komentarza, ale jego koniec wyznacza koniec wiersza.

Jak widać, dzięki komentarzom można sprawić, że nawet najprostszy program będzie wyglądał jakby był duży, brzydki i niesamowicie skomplikowany.


Są jeszcze inne sytuacje, w których może dojść do automatycznej konwersji typów. Gdy do łańcucha doda się wartość niebędącą łańcuchem, wartość ta przed dołączeniem zostanie przekształcona w łańcuch. Jeśli pomnożymy liczbę przez łańcuch, JavaScript spróbuje przed wykonaniem działania zamienić ten łańcuch na liczbę.

show("Apollo" + 5);
show(null + "ify");
show("5" * 5);
show("truskawka" * 5);

Ostatnia instrukcja spowodowała wydrukowanie napisu NaN, który oznacza specjalną wartość. Jest to skrót od angielskich słów „Not a Number” (nie liczba) i co ciekawe, jest to typ liczbowy. W tym przypadku dowiadujemy się, że truskawka nie jest liczbą. Wynikiem wszystkich działań arytmetycznych na wartości NaN jest NaN i dlatego właśnie w wyniku mnożenia przez 5 otrzymaliśmy NaN. Ponadto zapewne zaskoczy Cię fakt, że wynikiem porównania NaN == NaN jest false. Do sprawdzania czy dana wartość jest NaN można używać funkcji isNaN. NaN to kolejna (i już ostatnia) wartość, która w wyniku konwersji na typ logiczny zamienia się w false.

Te automatyczne konwersje bywają naprawdę wygodne, ale zasady ich wykonywania są dziwaczne i stanowią źródło wielu błędów. Mimo że + i * to operatory arytmetyczne, w tym przykładzie oba zachowują się całkiem inaczej. We własnych programach często używam operatora + do łączenia łańcuchów i „niełańcuchów”, ale operatora * i innych operatorów arytmetycznych lepiej na łańcuchach nie używać. Konwersja liczby na łańcuch zawsze jest możliwa i łatwa, natomiast zamiana łańcucha w liczbę może się nie udać (tak jak w ostatnim wierszu kodu w przytoczonym przykładzie). Aby jawnie przekonwertować łańcuch na liczbę, można użyć funkcji Number i wówczas oświadczamy, że jesteśmy świadomi ryzyka otrzymania wartości NaN.

show(Number("5") * 5);

W części poświęconej operatorom logicznym && i || napisałem, że operatory te tworzą wartości logiczne. Muszę przyznać, że to było uproszczenie. Jeśli zastosuje się je do wartości logicznych, to rzeczywiście zwrócą wartość logiczną. Ale można je stosować także do innych typów danych i wówczas zwracają jeden ze swoich argumentów.

Operator || tak naprawdę najpierw sprawdza wartość znajdującą się po jego lewej stronie. Jeśli w wyniku konwersji tej wartości na typ logiczny otrzyma true, zwraca tę wartość znajdującą się po jego lewej stronie. W przeciwnym przypadku zwraca wartość znajdującą się po prawej. Możesz to sam sprawdzić. Dlaczego tak jest? Jest to przydatne w praktyce. Rozważmy poniższy przykład:

var input = prompt("Jak się nazywasz?", "Kilgore Trout");
print("No to cześć, " + (input || "żabko"));

Jeśli użytkownik naciśnie przycisk Anuluj albo zamknie okno dialogowe prompt w jakiś inny sposób bez podawania swojego nazwiska, zmienna input będzie miała wartość null lub "". Obie te wartości po przekonwertowaniu na typ logiczny dałyby false. Wyrażenie input || "żabko" można w tym przypadku przeczytać następująco: „wartość zmiennej input lub łańcuch "żabko"”. Jest to łatwy sposób na zdefiniowanie wartości „awaryjnej”.

Operator && działa wg podobnych zasad, tylko odwrotnie. Gdy po jego lewej stronie znajduje się wartość dająca false po konwersji na typ logiczny, zwraca tę wartość. W przeciwnym przypadku zwraca wartość znajdującą się po prawej.

Kolejną ciekawą właściwością tych operatorów jest to, że wyrażenie znajdujące się po prawej stronie jest obliczane tylko wtedy, gdy jest to konieczne. W przypadku true || X nie ma znaczenia, jaka jest wartość X, ponieważ wynik zawsze będzie true, a więc wartość X nigdy nie będzie obliczana. A jeśli to wyrażenie miałoby jakieś skutki uboczne, to nigdy byśmy ich nie zaobserwowali. To samo dotyczy wyrażenia false && X.

false || alert("Niech się dzieje!");
true || alert("Nic się nie dzieje.");

Przypisy

  1. Bit to dowolny byt mogący mieć jedną z dwóch wartości, które zazwyczaj opisuje się jako 0 i 1. W komputerze wartości te są reprezentowane przez wysoką i niską wartość ładunku elektrycznego, silny i słaby sygnał albo lśniącą lub matową powierzchnię na płycie CD.
  2. Jeśli spodziewałeś się tu zobaczyć wartość 10010000, to dobrze myślisz. W JavaScripcie liczby nie są jednak przechowywane jako liczby całkowite.
  3. Tak naprawdę to 53, ponieważ stosowana jest sztuczka pozwalająca uzyskać jeden dodatkowy bit. Jeśli interesują Cię szczegóły, zajrzyj do standardu IEEE 754.
  4. Gdy wpiszesz wartości łańcuchowe w konsoli, zostaną one zwrócone z cudzysłowami i ukośnikami, dokładnie tak, jak zostały wpisane. Aby poprawnie wyświetlić specjalne znaki, można napisać print("anb") ― wkrótce wyjaśnię, dlaczego tak jest.
  5. Wór na bity to miejsce, w którym przechowywane są stare bity. W niektórych systemach programista musi własnoręcznie go od czasu do czasu opróżniać. Na szczęście w JavaScripcie odzyskiwanie zasobów odbywa się w pełni automatycznie.

Autor: Marijn Haverbeke

Źródło: http://eloquentjavascript.net/1st_edition/chapter2.html

Tłumaczenie: Łukasz Piwko

Treść tej strony dostępna jest na zasadach licencji CC BY 3.0

4 komentarze

  1. Swietne tlumaczenie!
    Drobna uwaga na temat funkcjonalnosci: jesli od przypisow moznaby bylo powrocic dokladnie do tego punktu, gdzie znajdowal sie odnosnik do przypisu, znaczynie ulatwiloby to plynne czytanie tekstu. Szukanie miejsca w ktorym sie zaprzestalo czytac zdecydowanie obniza koncentracje. Pozdrawiam.

    Odpowiedz
  2. W przykładach opisujących działanie pętli for jest błąd. W wyjaśnieniu jest, że kod ma wyświetlać liczby podzielne przez 4 w nawiasie, a wykonuje się, że wszystkie inne są w nawiasie, a podzielne przez 4 nie.
    Pozdrawiam.

    Bardzo podoba mi się Wasz kurs

    Odpowiedz
  3. W ćwiczeniu 2.2 jest błąd. „napisz program obliczający i wyświetlający wynik działania 210 (2 do potęgi 10).” Powinno być 1024 a nie 210.

    Odpowiedz

Dyskusja

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *