Czasami niektóre błędy są bardzo trudne do zdebugowania, bo występują stosunkowo rzadko, a i czają się w miejscach, w których byśmy się ich nie spodziewali.
Na początku pobieramy przycisk (1) i obydwa boksy (2, 3). Następnie dołączamy nasłuchiwanie na kliknięcie przycisku (4). Dla pewności blokujemy domyślną akcję (5) – jakby ktoś wsadził kiedyś ten mechanizm do formularza – a następnie iterujemy po wszystkich boksach (6) i nadajemy każdemu z nich odpowiednią klasę (7). Ot nic specjalnego.
Tylko że nie działa i przeglądarki pokazują jakieś dziwne błędy. W Chrome dostajemy:
Uncaught TypeError: Cannot read properties of undefined (reading '#<HTMLDivElement>') at HTMLButtonElement.<anonymous>
Natomiast w Firefoksie błąd jest jeszcze dziwniejszy:
Uncaught TypeError: evt.preventDefault() is undefined <anonymous>
Sytuacja z błędami jest paradoksalna o tyle, że osobiście uważam, że błąd w Chrome’ie dokładniej opisuje to, co się dzieje, ale jest przez to także mniej przyjazny dla użytkownika i trudno wywnioskować z niego, co wybuchło. Natomiast błąd w Firefoksie na pierwszy rzut oka nie ma sensu, za to wskazuje linijkę, w której doszło do eksplozji:
JavaScript
evt.preventDefault()
Jeśli się ją zakomentuje lub usunie, kod zaczyna działać. Ale naprawić można go też wprowadzając prostą modyfikację do problematycznej linii:
Magia, jaka jest za to odpowiedzialna, nazywa się ASI – Automatic Semicolon Insertion (Automatyczne Wstawianie Średników). To mechanizm obecny w JavaScripcie, który – jak sama nazwa wskazuje – służy do wstawiania średników na koniec poszczególnych wyrażeń i instrukcji w razie, gdyby programista tego nie zrobił. Działa to w miarę dobrze… dopóki nie natrafia na konstrukcje składniowe, które można rozumieć na wiele sposobów. I właśnie w tym wypadku z taką mamy do czynienia. W JS-ie bowiem nawiasy kwadratowe służą do dwóch rzeczy – tworzenia tablic oraz odwoływania się do właściwości obiektów:
JavaScript
const iAmAnArray = [];someObj[ 'I am just a property name' ];
ASI (jeszcze) nie korzysta ze sztucznej inteligencji i nie jest w stanie rozróżnić między tymi dwoma przypadkami. W naszym przykładzie oczekiwalibyśmy takiego rezultatu:
Średnik powinien być wstawiony po wywołaniu evt.preventDefault() (1), żeby zaznaczyć, że dalej mamy do czynienia z tablicą. ASI jednak ten średnik omija i wstawia dopiero po całym forEach (2). Innymi słowy uzyskujemy konstrukcję podobną do:
JavaScript
evt.preventDefault()[ box1, box2 ] // itd.
W tym momencie program działa tak, jakbyśmy chcieli pobrać właściwość z wartości zwracanej przez evt.preventDefault(). A evt.preventDefault() nic nie zwraca – czyli próbujemy pobrać właściwość z undefined. Z kolei wnętrze tablicy traktowane jest jako dwa wyrażenia rozdzielone operatorem przecinka, czyli jako nazwa właściwości traktowany jest ostatni z elementów (box2). Jeśli podstawimy sobie te wartości do kodu, uzyskamy:
JavaScript
undefined[ box2 ] // itd.
Czyli dokładnie to, o czym mówi błąd w Chrome: próbujemy pobrać z undefined właściwość o nazwie stworzonej z elementu div .
Dygresja
Co Bardziej Rozgarnięty Czytelnik zapewne zapyta w tym momencie, czy ten błąd by się pojawił, gdybyśmy zamiast wymieniać poszczególne elementy w tablicy, użyli zapisu [ ...document.querySelectorAll( '.box' ) ]. Otóż nie, ten błąd by się nie pojawił, za to rzucony byłby błąd składni. Nie można bowiem użyć mechanizmu spread w nazwie właściwości.
Ten blog wcześniej korzystał z systemu komentarzy Disqus. Jednakże pożegnaliśmy się i postanowiłem, że zaimportuję do nowej wersji stare komentarze z niego. Cóż, jego system eksportu na wiele nie pozwala…
Opublikowany:
Autor:
mikulew
Pierwszy raz z tym mechanizmem natknąłem sie czytając ten artykuł w TypeOfWeb. Też podobny przypadek i wniosek.
Dlatego dla mnie średnik jest "świętą krową" i trzeba zwracać na to uwagę, żeby uniknąć takich smaczków spowodowanych przez ASI.
Komentarze
Przejdź do komentarzy bezpośrednio na Githubie.
Dawne komentarze
Ten blog wcześniej korzystał z systemu komentarzy Disqus. Jednakże pożegnaliśmy się i postanowiłem, że zaimportuję do nowej wersji stare komentarze z niego. Cóż, jego system eksportu na wiele nie pozwala…
Pierwszy raz z tym mechanizmem natknąłem sie czytając ten artykuł w TypeOfWeb.
Też podobny przypadek i wniosek.
Dlatego dla mnie średnik jest "świętą krową" i trzeba zwracać na to uwagę, żeby uniknąć takich smaczków spowodowanych przez ASI.