TS TworcaStron.pl.

Średniki w JavaScript – wstawiać czy nie?

Temat prawie tak samo drażliwy jak ten czy klamra { za if() powinna być w tej samej czy kolejnej linii.

Średniki w JavaScripcie są opcjonalne, poza paroma sytuacjami, gdzie są obowiązkowe. Brzmi prosto, prawda? W takim raze czy należy ich używać czy nie?

Automatic Semicolon Insertion (ASI)

W JavaScripcie mamy wbudowany pewien mechanizm o nazwie Automatic Semicolon Insertion, który potrafi wstawiać średniki za nas.

Niestety nie jest specjalnie inteligenty, więc to nie jest tak, że o średnikach można zapomnieć. Ale nie ma co panikować, jego zasada działania jest bardzo prosta. ASI przeszukuje kod z lewej do prawej i postępuje wg kilku kroków:

  1. Jeżeli trafi na coś gramatycznie niepoprawnego (np. const a = 2 const b = 3 – po „2” nie spodziewa się „const”), ale pomiędzy tymi rzeczami jest nowa linia – wstawia średnik
  2. Jeżeli nie ma nowej linii ale niepasujący znak to „}” – wstawia średnik
  3. Na końcu pliku – wstawia średnik
  4. Jeżeli po instrukcjach typu continue, break, throw, return jest nowa linia – wstawia średnik w tej samej linii!!!
  5. Jeżeli po powyższych instrukcjach jest znak „}” – wstawia przed nim średnik

To znaczy, że jeżeli mamy taki kod:

const a = 2
const b = 4

function test() {
 return 
   4
}

function test2() {
 return}

function test3() {
 const a = 2}

Wg wyżej wymienionych zasad zostaną wstawione średniki w ten sposób:

const a = 2;
const b = 4;

function test() {
 return;
   4;
}

function test2() {
 return;}

function test3() {
 const a = 2;}

Zagrożenia

Tak jak widaż na przykładzie, mamy pewne zagrożenia widoczne przy słowie „return”. Tak jak jest napisane w pkt. 4 – jeśli po słowie return jest nowa linia, ASI wstawi średni w tej samej. Co prowadzi do tego, że funkcja zwróci „undefined” zamiast naszej wartości! To znaczy, że wartość po tych słowach kluczowych zawsze musi być zwracana w tej samej linii (lub zaczynać się od tej samej linii), np:

return 42

return 'hello'

return {
    'name': 'John',
    'surname': 'Snow'
  }

return [
   1, 2, 3, 4, 5
]

Mamy też inne zagrożenia, np. jeśli używamy nawiasów klamrowych [] lub zwykłych (), one mogą być widziane jako składniowo poprawne, np:

const a = 3
[1,2,3].map()

// zostanie odczytane jako
const a=3[1,2,4].map()
const b = 'hello'
(1 + 2).valueOf()

// zostanie odczytane jako
const b = 'hello'(1 + 2).valueOf()

Dlaczego tak się dzieje? Dlatego, że taki zapis jest poprawny. Przecież tak wygląda odwołanie do tablicy, albo wywołanie funkcji. Gramatycznie zapis wygląda jak jedna całość.

Aby się przed tym zabezpieczyć wystarczy przed tymi nawiasami wstawić średnik. Czyli powiedzieć JSowi, że to tutaj zaczyna się nowa instrukcja.

const a = 3
;[1,2,3].map()

I to są najczęstsze problemy, gdy nie stawiamy średników. Na szczęście łatwo im zaradzić.

„To, że czegoś możemy nie robić nie znaczy, że nie powinniśmy”

Taki argument brzmi jakby Automatic Semicolon Insertion (ASI) to była rzecz, którą można wyłączyć. Jakby to był dodatek, którego nie powinniśmy używać. 

Nie mniej, należy się pogodzić z myślą, że to coś w JavaScripcie jest i nawet używając średników trzeba to znać i na to uważać. Więc skoro i tak to jest, i tak trzeba to znać, i tak trzeba na to uważać, to dlaczego tego nie używać?

Średniki cię nie uratują

Stawianie średników  w każdej linijce zabezpieczy cię maksymalnie przed kilkoma stytuacjami. Czy warto, więc mieć dodatkowy znak przy prawie każdej linijce dla kilku prostych przypadków? Sporo osób powie, że tak. Sporo, że nie. 

Wstawiać średniki czy nie?

Gdy wejdzie sie na repozytorium Reacta widzać, że wstawiają. Ale w repo Vue.js już tych średników nie ma.

Decyzja należy do ciebie. Osobiście w większości swoich projektów ich nie wstawiam. Kiedyś wstawiałem, więc można się przestawiać. Dla mnie kod jest czytelniejszy (chociaż, gdy przechodziłem na JSa z PHP miałem odwrotnie wrażenie. Ponieważ tam są średniki. Kwestia przyzwyczajenia.)

Jednocześnie w większości projektach firmowych stawiam średniki. Dlatego, że tak było uzgodnione. Jak zawsze należy się dostosować do teamu i projektu.

Operatory && oraz || (and, or) w JavaScript

Kolejny odcinek, który miał trwać kilka minut a ma ponad 20. Okazuje się, że nawet podstawowe operacje w JavaScripcie mogą być wykorzystane do całkiem zaawansowanych rzeczy.

Dziś o kolejnych dwóch operatorach: && oraz ||.

Czym są operatory logiczne &&, ||

Można powiedzieć, że operator logiczny to funkcja, która przyjmuje 2 wartości i zwraca jeden wynik, zazwyczaj true albo false.

Kilka przykładów:

const a = true && true // zwraca true
const b = false && true  // zwraca false
const c = true && false // zwraca false
const d = false && false  // zwraca false

const a = false || false // zwraca false
const b = true || false // zwraca true
const c = false || true // zwraca true
const d = true || true // zwraca true

Zasada działania jest prosta i logiczna. Gdy mamy operator && (and) spełnione muszą być wszystkie warunki, tzn. wszystkie parametry muszą być prawdziwe. Jeśli chociaż jeden nie będzie spełniony, operator nie sprawdza dalej i zwraca false.

W operatorze || (or), conajmniej jeden parametr musi być prawdziwy. Jeśli taki jest to nie ma sensu sprawdzać drugiego operatora bo wymóg został już spełniony. Tak się właśnie dzieje. Gdy operator trafia na pierwszą prawdziwą wartość, nie sprawdza już dalej bo dostał to czego chciał.

Co przyjmują parametry logiczne?

W poprzednich przykładach używałem wyłącznie parametrów booleanowskich, np. true && true // zwraca true

Ale, operatory te przyjmują KAŻDĄ dowolną wartość. Równie dobrze to może być 23 && 'to jest tekst’ . Pojawia się wtedy pytanie, jak operator rozpoznaje czy wartość jest true czy false, czy spełnia warunek czy nie?

Otóż operator wcale nie sprawdza czy wartość jest true/false. Sprawdza czy wartość jest prawdziwa/fałszywa – uwaga! Słowa klucz!.

Wartości prawdziwe (truthy) i fałszywe (falsy)

Maksymalnie krótko, wartość prawdziwa to taka, która po konwersji na boolean daje true, np.

Boolean(’hello’) => true
Boolean(1) => true
Bollean({}) => true

Natomiast wartość fałszywa to taka, która po konwersji na boolean daje false, np.

Boolean(null) => false
Boolean(0) => false

Żeby było jeszcze łatwiej zapamiętać, wszystkie wartości są prawdziwe, poza tymi pięcioma:

Każda inna po umieszczeniu w Boolean() zwróci true! Proste, prawda?

Czy operatory zawsze zwracają true albo false?

To jest najciekawsze. Te operatory wcale nie zwracają true i false. One zwracają wartości jednego z podanych parametrów! Dokładniej, w przypadku && zwraca wartość ostatniego prawdziwego parametruLub pierwszy fałszywy jeśli taki jest.

W przypadku || zwraca pierwszy prawdziwy parametr (stara się wykonać najkrótszą drogę). Jeśli nie ma prawdziwego, zwraca ostatni parametr (ponieważ szukając prawdziwego, przeszedł przez wszystkie i dotarł do ostatniego).

Np. jeśli mamy kod:

3 && 'hello' // 3 jest wartością prawdziwą i 'hello' jest wartością prawdziwą, więc kod zwróci 'hello'

34 && null // 23 jest wartością prawdziwą, 0 jest fałszywą, czyli zwróci null

'' && 'word' // pusty string jest wartością fałszywą, zwraca pusty string

'hello' || 'word' // 'hello' jest wartością prawdziwą, zwraca 'hello'

null || 34 // null jest wartością fałszywą, zwraca 34

Do czego to się przydaje

Korzystając z tego, że operatory zwracają jedną z podanych wartości, można to wykorzystać do szybkiego tworzenia zmiennych, bez dodatkowych instrukcji warunkowych. 

Np. jeśli chcę stworzyć zmiennną, która przyjmuje wartość od innej zmiennej „userMoney”, lub nadać jej domyślną wartość = 18, mogę zrobić coś takiego:

 const money = userMoney || 100 

jeśli wartość z „userMoney” będzie wartością prawdziwą to operator || zwróci wartość z „userMoney”. Jeśli natomiast będzie fałszyła, operator zwróci pierwszą wartość prawdziwą czyli 100. 

Inny przykład. Który ma bardzo częste zastosowanie w prawdziwym kodzie: mamy obiekt „cat” a w nim wartość „name”, do której chcemy się później odwołać. 

const cat = { name: 'Flerkin'}

const catsName = cat.name

Ale co jeśli okaże się, że obiekt „cat” będzie pusty? Odwołanie do cat.name zwróci błąd. 

const cat = null

const catsName = cat.name // błąd: Cannot read property 'name' of null

Możemy się przed tym łatwo zabezpieczyć.

const cat = null

const catsName = cat && cat.name // zwraca 'null' ale nie błąd

Możemy jeszcze dopisać, że jeśli pobranie nazwy obiektu „cat” się nie uda to nadaj mu wartość domyślną, np. „Mruczek”

const cat = null

const catsName = cat && cat.name || "Mruczek"

Tak jak widać, proste operatory a możemy użyć je w już niekoniecznie prosty sposób. Zachęcam do obejrzenia wideo z początku wpisu, tam staram się to dokładniej opisać:)

== vs ===, czyli o koercji typów w JavaScript

Pierwszy odcinek z serii zrozumieć JavaScript będzie o operatorach? Tak,  głównie o tym pierwszym, czyli podwójnym.

Nie jest to odcinek stricte o koercji typów, bo omawiam je na konkretnym przykładzie, na przykładnie operatora „==”. Ale to wystarczy żeby załapać zasadę działania tego mechanizmu.

Chciałem, żeby ten odcinek miał max. 10 minut… wyszło ponad 20 a wydaje mi się, że mówiłem zwięźle. Myślę, że to najlepszy dowód, że zwykłe porównanie operatorów wcale nie jest takie proste!

== vs ===

Mówiąc prostym językiem jaka jest różnica pomiędzy tymi operatorami? Ten potrójny jest dokładniejszy. Natomiast ten podwójny jest mniej dokładny.

Potrójny operator porównania … === …

Teraz trochę dokładniej. Potrójny operator sprawdza czy wartości po obu stronach są IDENTYCZNE

Identyczne to znaczy czy ich wartość się zgadza, np. czy to jest 5, czy 4, czy ’hello’, oraz czy zgadza się ich typ, czyli czy jest to tekst, liczba czy boolean.

'2' === 2 // false
2 === 2 // true

Pierwszy przykład zwróci false, ponieważ ’2′ jest stringiem, czyli tekstem bo jest w cudzysłowie. Natomiast drugi parametr 2 jest typu number, czyli jest liczbą. Wynika z tego, że oba parametry nie są IDENTYCZNE. W drugim oba parametry mają taką samą wartość (2) oraz są tego samego typu (number), mówiąc prościej – są identyczne.

Podwójny operator porównania … == …

Tutaj zaczyna się dziać trochę magii. W podwójnym operatorze porównania JS zaczyna trochę myśleć „samodzielnie”. Próbuje pomóc i w kilku krokach zmienia typy parametrów na takie, aby na końcu zwrócić true (o ile się da).

Np. jeżeli mamy '2′ == 2 to JS zmieni typ pierwszego parametru na number. Sam możesz to zrobić w konsoli przeglądarki, wpisz Number(’2′). Wtedy kolejny krok wygląda już tak 2 == 2. Jeśli parametry są tego samego typu (oba są typu number) to następuje kolejny krok, czyli porównania potrójne 2 === 2, a to wiemy już, że zwróci true, prawda?

Koercja typów

Nie działa jednak kompletnie chaotycznie, chociaż na pierwszy rzut oka może się tak wydawać. Proces zmiany typu parametru nazywany jest koercją typów. Trudne słowo, które ma proste znaczenie: zmiana typu wartości na inny typ. 

W podwójnym operatorze zmiana tych typów następuje wg ściśle określonych 10 króków:

  1. Jeśli oba parametry są tego samego typu porównaj je jeszcze raz potrójnym operatorem   … === …
  2. Jeśli pierwszy jest null a drugi undefined, zwróć true. To trzeba zapamiętać. 
  3. Odwrotność drugiego. Pierwszy to undefined, drugi null. Jak można się domyśleć, także zwróci true.
  4. Jeśli pierwszy jest number a drugi string. Stara się zmienić tekst na liczbę (w poprzednim akapicie wspomniałem jak można to zrobić). Następnie powtarza wszystkie kroki od początku.
  5. Odwrotność czwartego. Czyli pierwszy to string a drugi number. Zmienia typ stringu na number i powtarza wszystkie kroki.
  6. Jeśli pierwszy jest boolen a drugi czymkolwiek. Stara się zmienić boolean na number i powtarza wszystkie kroki (Number(true) = 1, Number(false) = 0).
  7. Odwrotność szóstego.
  8. Jeśli pierwszy jest wartością prymitywną (string, number, symbol) a drugi obiektem. Stara się zmienic obiekt to wartości prymitywnej i powtarza wszystkie kroki. Jak zmenić 
  9. Odwrotność ósmego.
  10. Jeżeli żaden z poprzednich punktów nie jest naszym przypadkiem, zwóć false.

Powyższe 10 kroków to jedna iteracja, czyli jedno powtórzenie. Tych powtórzeń będzie tyle, aż w końcu dostaniemy true albo false. Ot, cała magia.

 

Kilka przykładów:

'2' == 2 // krok 5, ponieważ pierwszy parametr to string, drugi to number
2 == 2 // krok 1, poniważ oba parametry są tego samego typu (number)
2 === 2 // zwraca true, koniec koercji
null == undefinied // krok 2, zwróci true, koniec porównania
[] == 0 // krok 9, ponieważ pierwszy parametr to obiekt, następuje zmiana tablicy w wartość prymitywną (w tym przypadku w pusty string)
'' == 0 // krok 5, ponieważ pierwsza wartosć to string, druga to number, następuje zmiana stringu na number
0 == 0 // krok 1, ponieważ oba parametry są już tego samego typu
0 === 0 // zwraca true, koniec koercji

Po więcej przykładów i dokładniejsze wytłumaczenie zapraszam do filmu:)

Nowa seria „Zrozumieć JavaScript”

Dziś rusza kolejna mała seria na YouTubie, tym razem o JavaScripcie!

Tak, odcinków o JavaScripcie jest już sporo… Mam jednak wrażenie, że większość podchodzi do tematu w stylu „zrób to, a stanie się tak”. Ja natomiast chcę podejść do tematu od innej strony, czyli pokazać dlaczego dzieje się tak a nie inaczej. Poznać ten dziwny język bardziej od środka i zwyczajnie go zrozumieć.

Inny przykład? Ile frameworków znasz? Pewnie kilka, a czy potrafisz wyjaśnić jak one działają, dlaczego zachowują się tak a nie inaczej? 

To nie jest seria „jak nauczyć się JavaScriptu”, wymagana jest jednak jakaś podstawowa wiedza. Chociaż początkujący też mogą śmiało ją oglądać. Chcę pokazać co się dzieje pod maską JSa. 

O serii wpominałem na moim newsletterze, do którego serdecznie zapraszam, podobno warto 🙂  https://tworcastron.pl/zapis.html

Pierwszy rzeczowy odcinek już jutro!

 

Node.js – kurs w 60 minut

Strasznie długo zabierałem się do tego odcinka, ale w końcu udało mi się go nagrać 🙂

Odcinek jest przede wszystkim dla tych, którzy z nodem jeszcze nie pracują, ale założę się, że zaawansowani też mogą dowiedzieć się kilku ciekawostek.

Np, czy wiesz:

Na te i inne pytania odpowiadam w poniższym odcinku 🙂

Zapraszam cię również do bardzo powiązanych filmów, czyli:

MongoDB + Node.js – kurs w 60 minut

Jak postawić aplikację Node.js na własnym serverze