podwojna negacja bang bang js @dustbarnes ach te internety

Podwójna negacja (aka !!) w JS

Gorący temat. Najbardziej kulturalne dyskusje kończy rękoczynami i rzucaniem internetowych klątw na odważnych entuzjastów podwójnej negacji. A ja cenię sobie spokojne życie, więc na wstępie chciałabym zaznaczyć fundamentalny cel tego artykułu. Tak więc, drogi Czytelniku, dziś dzielę się z Tobą wiedzą, a nie rekomendacją.

Teraz, ze spokojnym sercem, mogę przejść do rzeczy.

Czym jest podwójna negacja?

Inaczej double negation, double bang, bang bang, NOT NOT, !!.

Przede wszystkim !! nie jest operatorem logicznym.

!! jest podwójnym zastosowaniem operatora logicznego NOT, czyli !.

Podwójną negację wykorzystuje się – ogólnie mówiąc – w celu konwersji na typ prosty boolean (true lub false).

Dygresja: Operator logiczny NOT

Przypomnijmy sobie, jak działa operator logiczny NOT. Zwraca on false wtedy, kiedy jego argument konwertuje się do true. W innym przypadku zwraca true.

Oto JEDYNE* wartości, które w świecie JS są falsy, czyli konwertują się do false lub są równoznaczne false.

  • false
  • null;
  • NaN;
  • 0;
  • pusty string ("" / '' / ``);
  • undefined.

Sposób działania podwójnej negacji

Z powyższą wiedzą dotyczącą ! sposób działania !! jest oczywisty. Pierwsza negacja to konwersja jakiejkolwiek wartości na true albo false, a następnie odwrócenie jej. Druga negacja otrzymanej wartości boolean sprawia, że otrzymujemy rzeczywistą reprezentację początkowej wartości w świecie wartości typu boolean. I wszystko jasne, prawda?

!!x zwróci false, kiedy wartość x to null/NaN/undefined/0/""/false
!!x zwróci true, kiedy wartość x to jakakolwiek inna, niewymieniona powyżej wartość.

Kilka przykładów:

!!false === false
!!true === true

!!0 === false
!!1 === true
!!-1 === true
!!parseInt("foo") === false // NaN is falsy

!!"" === false // empty string is falsy
!!"foo" === true  // non-empty string is truthy
!!"false" === true  // ...even if it contains a falsy value

!!undefined === false
!!null === false

!!{} === true  // an (empty) object is truthy
!![] === true  // an (empty) array is truthy

Przykładowe zastosowanie

Podwójną negację można wykorzystać, aby mieć pewność, że zwracana wartość będzie typu boolean. Jest to przydatne podczas zastosowania owych wartości jako argumenty w konstrukcjach warunkowych.

REACT CASE

Przejdę teraz do sedna. Pierwszy raz spotkałam się z podwójną negacją właśnie w projekcie opartym na bibliotece React. Spójrzmy na poniższy przykład, w którym chcemy powiadomić użytkownika o pojawieniu się nowych, nieprzeczytanych wiadomości. Z życia wzięte.

{unreadMessages.length && <div>You have {unreadMessages.length} unread messages!</div>}

Dygresja: Operator logiczny AND

Przypomnijmy sobie, jak działa operator logiczny &&.

Ten operator jest wykorzystywany z co najmniej 2 argumentami. Zwraca wartość jednego ze swoich argumentów, więc jeśli któryś z tych operatorów jest użyty z wartością nie-logiczną, to zwróci wartość nie-logiczną. Zwracaną wartością będzie ostatni argument dla przypadku, kiedy wszystkie argumenty konwertują się do true lub pierwszy argument, który konwertuje się do false.

W skrócie:

expr1 && expr2

zwróć expr1, jeśli jest konwertowany do false; w innym wypadku, zwróć expr2.

Wróćmy teraz do naszego React’owego przykładu wyświetlania powiadomienia o nieprzeczytanych wiadomościach. Powiedz mi, co wyświetli się dla przypadku, kiedy ich nie będzie (unreadMessages=[]) ?

{unreadMessages.length && <div>blabla</div>}

Oczywiście nasza aplikacja odważnie wyświetli 0. I będzie miała rację.

unreadMessages.length zwróci 0, które – zgodnie z tym, co ustaliliśmy wcześniej – konwertuje się do false.
Wobec tego nasze wyrażenie zwróci pierwszą falsy wartość, czyli nieszczęsne 0.

Jak to naprawić?

  • Naszym bang bang operatorem:
    !!unreadMessages.length, czyli !!0 konwertuje się do false. Ostatecznie aplikacja nic nie wyrenderuje.
  • Dokumentacja jednak sugeruje poniższe, klasyczne rozwiązanie:
    {unreadMessages.length > 0 && <div>...</div>}
  • Wśród nas znajdą się pewnie zwolennicy i takiej metody…
    {unreadMessages.length ? <div>...</div> : false}
    Zainteresowanych słusznością użycia tego operatora warunkowego odsyłam do mojego innego artukułu.

Zawsze jest jakieś ALE…

Podwójna negacja nie cieszy się dobrą reputacją w środowisku programistów. Zdecydowana większość krzyczy, iż taka składnia jest nieczytelna.

Jak dla mnie – niekoniecznie.

Nie będę wkładać kija w mrowisko (nie dziś), więc nie zdradzę Ci moich rekomendacji. W zamian za to zapoznam Ciebie z alternatywami podwójnej negacji.

1. Boolean()

Podwójną negację !!(expr) można zastąpić jawną konwersją do typu boolean, czyli: Boolean(expr).

Boolean(3) === !!3;

Pamiętaj jednak o poprawnej składni!

!!new Boolean(false) // true
!!Boolean(false) // false

Wywołanie new Boolean(false) tworzy nowy obiekt. A każdy obiekt w JS konwertuje się do true (nawet jeśli zawiera w sobie falsy wartość).
Dopiero wywołanie funkcji Boolean(false) zadziała w oczekiwany przez nas sposób, zwracając wartość prymitywną.

2. Optional chaining operator

Opisywałam go już na blogu o tutaj. Artykuł jest nieco zamierzchły, z czasów, kiedy operator był jeszcze w Stage 1. Mimo wszystko sposób jego działania nie zmienił się od tamtej pory.

Optional chaining operator z pewnością przyda się podczas sprawdzania istnienia własności obiektu.

W skrócie:

obj?.prop – jeśli obj jest równe null lub undefined – całe wyrażenie jest konwertowane do undefined

3. .length

Opisane w powyższym przykładzie – stare, dobre sprawdzanie długości tablicy.

4. Wiara…

…w automatyczną, niejawną koercję (implicit coercion). JS sam próbuje dokonywać konwersji, kiedy spotyka się z niespodziewanym typem.

Na przykład:

if (-1) // true
if ("0") // true
if ({}) // true

Jednak ślepa wiara w koercję JSa nie jest najlepszą praktyką wśród programistów. JS z pewnością ma dobre intencje, jednak efekty jego działania mogą być dalekie od naszych oczekiwań.


A teraz ustępuję miejsca osobom, które chcą wziąć udział w dyskusji na temat (nie)słuszności podwójnej negacji. Korzystacie z niej? Dlaczego tak, dlaczego nie? Jakie znacie alternatywy? Piszcie w komentarzach, jestem ciekawa Waszych odpowiedzi!


Przypisy:
* >> patrz komentarze


Źródła:
KLIK, KLIK + wszystkie odnośniki, które pojawiły się bezpośrednio w tekście.

Artykuły, które mogą Ci się spodobać...

Wpisz hasło, którego szukasz i naciśnij ENTER, aby je wyszukać. Naciśnij ESC, aby anulować.

Dawaj na górę