Debugowanie kodu

Pisząc kod co chwila będziemy natrafiać na różnego rodzaju błędy. Aby sobie z nimi radzić powinniśmy umieć debugować nasz kod. Poniżej zajmiemy się kilkoma metodami podejścia do takiego debugowania.

console

To chyba najprostsza metoda do debugowania kodu, którą już trochę omawialiśmy w rozdziale o debugerze a i którą non stop używam w listingach.


//wypisanie tekstu
const text = "Moja mała świnka";
console.log(text);

//wypisywanie całego obiektu
console.log(window);

//wypisanie wielu rzeczy po przecinku
const a = 10;
const b = 20;
console.log('a: ', a, 'b: ', b);

Tutaj mała uwaga. Używajmy przecinka jeżeli chcemy wypisać coś z tekstem. Dzięki temu wypisywany element nie będzie konwertowany na tekst i nie dostaniemy w konsoli głupot.


const obj = { name: "test" }

//zamiast
console.log("Wynik: " + obj); //"Wynik: [object Object]"

//użyj
console.log("Wynik:", obj); //"Wynik:", {name: "test"}

Ciekawą sztuczką jest wypisywanie obiektu z kluczem o danej nazwie. Dzięki temu łatwiej się można odnaleźć na co patrzymy w konsoli:


const myObj = { }
console.log({myObj : myObj});
W edytorze Visual Studio Code Warto zainstalować dodatek ES6 Code Snippets, który dodaje nam skróty takie jak pokazał autor na StackOverflow.
Natomiast w Webstormie idealnie sprawdza się skrót .log. Piszemy coś co chcemy wypisać, dopisujemy .log i naciskamy klawisz tab. Tekst zostanie okryty console.log().
W innych edytorach nawet jeżeli nie znajdziemy odpowiedniego dodatku (a pewnie znajdziemy), to zawsze można użyć snippetów.

Obiekt console udostępnia nam też kilka innych - równie przydatnych metod, które opisane są na stronie https://developer.mozilla.org/pl/docs/Web/API/Console.

Jeżeli zapomnimy ich listę, wystarczy w konsoli debugera wpisać słowo console i zbadać wypisany obiekt. W pracy codziennej w większości przypadków wystarczą:


console.log() //klasyk
console.dir() //do bardziej szczegółowego wypisywania obiektów. Szczególnie przydatna przy wypisywaniu elementów DOM
console.table() //wypisuje ładnie sformatowaną kopię danej tabeli

Debugowanie za pomocą debugera

Debugowanie za pomocą console sprawdza się w wielu sytuacjach, i wcale nie trzeba się go wstydzić. Nie zawsze jest jednak najbardziej optymalną metodą odpluskwiania kodu.

Gdy pojawi się jakiś komunikat w konsoli, błąd lub informacja, po prawej stronie będziemy mieli pokazaną linię w kodzie, z której nastąpiło wypisanie danej informacji. Gdy ją klikniemy zostaniemy przeniesieni do zakładki Sources, którą omawialiśmy w rozdziale o debugerze.

debuger console nr linii debugowanie zakładka sources

Aby debugować kod, musimy zatrzymać nasz skrypt w danym miejscu. Możemy to zrobić na kilka sposobów.

Możemy użyć w kodzie słowa kluczowego debugger:


const ob1 = { ... }
const ob2 = { ... }
debugger; //zatrzymujemy wykonywanie kodu

const ob3 = {...ob1, ...ob2};
debugger; //znowu zatrzymujemy - a co!

Kolejnym sposobem, który daje podobne efekty jest użycie tak zwanych breakpointów. Po kliknięciu w numer danej linii, stawiamy w niej breakpoint (niebieski znacznik). Jeżeli skrypt ponownie się wykona (np. strona zostanie przeładowana, dana funkcja zostanie odpalona itp.), skrypt zostanie zatrzymany w danej linii (dana linijka jeszcze się nie wykona, a kursor stanie na jej początku).

breakpoint

Po zatrzymaniu skryptu (dowolną metodą) najeżdżając na zmienne możemy badać ich aktualny stan:

breakpoint badanie zmiennej

W niektórych przypadkach możemy chcieć zatrzymywać kod na danym breakpoincie tylko w określonym warunku. Powiedzmy, że chcemy zbadać poniższych użytkowników, ale tylko w sytuacji, gdy ich punkty wynoszą poniżej 50:


const users = [
    { name : "Marcin", points : 51 },
    { name : "Weronika", points : 87 },
    { name : "Piotr", points : 65 },
    { name : "Monika", points : 46 },
    { name : "Anna", points : 98 },
]

users.forEach(function(user) {
    console.log(user.name, user.points);
})

Jeżeli teraz postawimy breakpoint w linijce z console.log(), skrypt będzie się zatrzymywać w tej linii przy każdej iteracji naszej pętli. Czasami (szczególnie w przypadku pętli po długich tablicach) może to być mocno problematyczne. Możemy jednak kliknąć na taki niebieski breakpoint prawym przyciskiem i wybrać "Edit breakpoint".

Pojawi się okienko, do ktorego możemy dodać dodatkowy warunek, który jeżeli będzie spełniony, aktywuje breakpoint.

Breakpoint zmieni kolor na pomarańczowy i od tej pory będzie aktywny tylko w przypadku gdy nasz warunek będzie spełniony.

Aby wznowić działanie skryptów kliknij ikonkę play po prawej stronie zakładki:

ikony breakpointów

lub naciśnij klawisz F8. Skrypt się wznowi i ewentualnie zatrzyma na kolejnym breakpoincie.

Jak widzisz poza ikonką wznawiającą działanie skryptów po prawej stronie mamy też kilka innych.

Wznawia działanie kodu
Jeżeli w danej linii znajduje się wywołanie funkcji, debuger wykona ją, ale nie przeniesie kursora do jej wnętrza, a przeskoczy nim do następnej instrukcji w kodzie. Jeżeli nie ma takiego wywołania, ikonka zachowa się jak przejście do następnego kroku.
Przechodzi kursorem do wnętrza funkcji, lub zachowuje się jak przejście do kolejnego kroku
Wychodzi z danej funkcji i przechodzi do kolejnej linii kodu (czyli poza linię, gdzie dana funkcja została wywołana)
Wyłącza na chwilę breakpointy. Pomocne gdy mamy nastawiane naście takich breakpointów i chcemy na chwilę sprawdzić jak się strona zachowuje bez zatrzymywania
Czy kod ma się zatrzymywać na błędach. Jeżeli włączymy tą opcję, kod będzie zatrzymywał się tuż przed pokazaniem czerwonych błędów w konsoli

Zauważ, że po ustawieniu breakpointów, pojawiają się one nie tylko jako niebieskie punkty na numerycznej linii, ale też jako lista w prawej części zakładki Sources:

breakpoints lista

Sekcja ta ma jeszcze kilka ciekawych rzeczy do zaoferowania, które są bardzo przydatne podczas debugowania.

Watch, CallStack, Scope i XHR

Pierwsza z góry to sekcja Watch.

debuger watch

Służy ona do obserwowania zmiennych. Za pomocą plusa możemy tam podać nazwę zmiennej, którą chcemy nasłuchiwać, a dzięki temu właśnie w tym miejscu będziemy mogli w wygodny sposób sprawdzić jej stan w określonym momencie.

Kolejna sekcja - CallStack - pokazuje kolejność odpalanych funkcji. Funkcje w Javascript (jak i w innych językach) mogą być odpalane z wnętrza innych funkcji itp. Tutaj możemy podejrzeć właśnie taką kolejność.

Bardzo użyteczna sekcja Scope pozwala nam łatwo sprawdzać zmienne w danym scope. Co to oznacza? Powiedzmy, że postawimy breakpoint wewnątrz danej funkcji. Dzięki tej sekcji w wygodny sposób sprawdzimy, jakie zmienne są używane wewnątrz tej funkcji.

Jeżeli chcesz sprawdzić jej działanie, kliknij w poniższy przycisk. Za pomocą instrukcji debuger postawiłem dla ciebie breakpoint w przykładowej funkcji. Po jego kliknięciu sprawdź gdzie zatrzymał się skrypt i co znajduje się w sekcji Scope.

Kolejną sekcją jest XHR/fetch Breakpoints. Możemy w niej ustawiać breakpointy dla requestów, które będą miały w adresie szukaną frazę.

DOM Breakpoints

Poza breakpointami i instrukcją debugger istnieją też inne sposoby na zatrzymanie skryptu w odpowiednim momencie.

Po pierwsze możemy nakazać przeglądarce zatrzymanie wykonywania skryptów, jeżeli zostanie zmodyfikowany dany element w drzewie DOM. Aby to zrobić wystarczy w zakładce debugera Elements kliknąć prawym przyciskiem myszy wybrany element, i z menu kontekstowego wybrać opcję Break on.

Menu kontekstowe po kliknięciu na element

Poszczególne opcje oznaczają:

subtree modification zatrzymaj działanie skryptów, gdy zmieni się struktura html danego elementu i jego zawartości (dzieci i zagnieżdżonych elementów)
attribute modifications zatrzymaj skrypty, jeżeli zmieni się zawartość atrybutów danego elementu (np. skrypt zmieni style danego elementu)
node removal - zatrzymaj skrypt jeżeli dany element zostanie usunięty

Sprawdźmy to na przykładzie.
Poniżej mamy 3 przyciski. Pierwszy po kliknięciu zmienia tło (czyli dodaje atrybut style). Drugi zmienia swoją zawartość html, a trzeci po kliknięciu jest usuwany.
Ustaw im odpowiednie "breake on". Zauważ, że po ustawieniu breake dla danego elementu, pojawi się przy nim w zakładce Elements niebieska kropka.

niebieskie kropki przy oznaczonych elementach

Jeżeli takie breakpointy bazujące na zmianach html ustawimy w zakładce Elements, pojawią się one też jako lista z prawej strony w zakładce Source.

debuger zadkładka breakon

Event Listener

Wracamy do zakładki Source. Poniżej sekcji Breakpoints znajduje się przydatna sekcja Event Listener Breakpoinst, która służy do ustawiania breakpointów na dane zdarzenia. Wyobraź sobie, że po kliknięciu, najechaniu kursorem czy dowolnej innej czynności wykonywany jest jakiś kod Javascript. Skąd mamy jednak wiedzieć, gdzie dana funkcja się znajduje? Wystarczy w sekcji tej znaleźć event Click (znajduje się w grupie Mouse) i go zaznaczyć. Od tej pory jeżeli pod dane zdarzenie podpięty jest jakiś kod, nasz kod zostanie w tym miejscu zatrzymany.

breakpoint on click

Sprawdźmy to. Włącz breakpoint dla zdarzeń click i kliknij w poniższy przycisk. Dzięki temu kod zostanie zatrzymany w super tajnej funkcji, której miejsca nikt nigdy nie odkrył (a warto)...

Opcja ta jest mega przydatna na obcych stronach, gdzie zaciekawi nas jakiś efekt. Wystarczy włączyć breakpoint na dane zdarzenie i zacząć badać...

Debugowanie w Visual Studio Code

Jeżeli używamy edytora Visual Studio Code, debugowanie naszego kodu możemy sobie dodatkowo nieco umilić. Istnieje bowiem dodatek zwący się Debugger for Chrome, który umożliwia debugowanie kodu bezpośrednio w edytorze.

Gdy go zainstalujemy, wystarczy przejść w tryb debugowania (klawisz F5 lub wybranie z górnego menu opcji Debug->Start Debuging), a naszym oczom ukaże sie widok z oknami o znaczeniu podobnym do tego, co omawialiśmy powyżej:

debugowanie w vsc

na górze lewej sekcji znajdują się opcje służące do skonfigurowania połączenia oraz jego odpalenia:

debugowanie w VSC - opcje do odpalenia

Po kliknięciu w trybik zostanie otwarty plik z przykładową konfiguracją.


{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "chrome",
            "request": "launch",
            "name": "Launch Chrome against localhost",
            "url": "http://127.0.0.6:8080",
            "webRoot": "${workspaceFolder}"
        }
    ]
}

Aby móc debugować w edytorze, musimy naszą stronę wcześniej odpalić na jakimś serwerze lokalnym.

Jeżeli pracujemy z jakimś frameworkiem JS - np. Reactem, praktycznie zawsze będziemy mieli odpalony taki serwer (np. react-dev-server).

Jeżeli pracujemy bardziej klasycznie, możemy skorzystać z jednego z wielu serwerów lokalnych. Serwerów jest multum. Osobiście polecam spróbować jedno z poniższych rozwiązań:

U mnie się sprawdza...

Gdy już odpalimy naszą stronę, musimy adres na jakim została odpalona wpisać w powyższej konfiguracji w parametrze url.

Po podaniu adresu i zapisaniu konfiguracji, możemy odpalić połączenie poprzez zieloną strzałkę. Gdy wszystko pójdzie dobrze, nasza strona powinna się otworzyć w nowym oknie przeglądarki.

Od tej pory możemy zacząć debugować bezpośrednio w edytorze. Oznacza to, że breakpointy możemy stawiać nie tylko w Source, ale też klikając obok numerów linii bezpośrednio w edytorze (pojawią się czerwone kropki), a i boczne okna staną się dla nas pomocne.

debugowanie w VSC
W innych edytorach także istnieje możliwość debugowania kodu w edytorze. Przykładowy film pokazuje jak debugować w programie Webstorm.

Quokka

Inne możliwości ułatwiające pracę z debugowaniem daje nam np. dodatek o nazwie Quokka. Po jego instalacji, będzie nam pokazywał wynik danego równania tuż obok linii.

quokka

Nie sprawdzi się to w każdym rodzaju skryptu, ale dość często może być całkiem wygodnym rozwiązaniem.

Trening czyni mistrza

Jeżeli chcesz sobie potrenować zdobytą wiedzę, to zadania do tego rozdziału znajdują się w w repozytorium pod adresem: https://github.com/kurs-javascript/js-podstawy w katalogu 6-debuger, przy czym śmiało możesz robić zadania z całego repozytorium.

Dowiedz się więcej na ten temat tutaj.

A może pasuje ci ściągnąć zadania do wszystkich rozdziałów na raz? Jeżeli tak, to skorzystaj z repozytorium pod adresem https://github.com/kurs-javascript/js-all.

Wszelkie prawa zastrzeżone. Jeżeli chcesz używać jakiejś części tego kursu, skontaktuj się z autorem. Aha - i ta strona korzysta z ciasteczek.