Если вы читаете это, возможно, вы студент или с тревогой смотрите на свой код, пытаясь понять, где что-то пошло не так. Или, возможно, вы просто надеетесь открыть для себя несколько полезных приемов для отладки вашего кода в будущем. В этом посте мы рассмотрим несколько методов, которые мы использовали для отладки своего кода, а также несколько простых примеров для подражания.
Но сначала, что такое отладка и как это будет применимо к вам?
Отладка – это процесс определения причин появления неожиданных результатов или ошибок в вашем коде. Независимо от того, являетесь ли вы студентом, пытающимся пройти модульные тесты по заданию, или опытным веб-разработчиком, отладка будет важнейшим компонентом вашего пути программирования. Не стоит ожидать, что весь ваш код сработает с первой попытки, а ошибки не означают, что вы плохой разработчик! На самом деле, научиться находить ошибки – бесценный навык, который сделает вас лучшим разработчиком.
Несколько советов, которые облегчат отладку вашего кода
- Сделайте свой код максимально выразительным или простым для понимания. Это может означать маркировку переменных и функций в соответствии с тем, что они выполняют (например, используя такие имена переменных, как
firstName
иlastName
, а неstring1
иstring2
). Это также может означать использование встроенных методов (таких как карта массива), которые подсказывают, какое возвращаемое значение следует ожидать. - Аналогичным образом, старайтесь, чтобы ваши функции выполняли одну главную цель. Это уменьшит количество ошибок и упростит понимание вашего кода.
- Объявляйте переменные и функции только там, где они необходимы. Если переменные и функции доступны с помощью кода, которому они не нужны (например, если они были объявлены в глобальной области видимости без необходимости), отследить ошибки будет намного сложнее.
- По возможности избегайте изменений. Бывают случаи, когда вам захочется изменить состояние массива или объекта, но, когда это возможно, лучше всего обновить их неразрушающим образом (например, создав копии перед изменением). В связи с этим также безопаснее объявлять свои переменные с помощью
const
, если только вы уже не знаете, что будете переназначать переменную по всему вашему коду. Это предотвратит непреднамеренное изменение значения переменной и вместо этого может привести к полезной ошибке, подобной этой:Uncaught TypeError: Assignment to constant variable.
- Часто тестируйте свой код по мере его написания. Гораздо сложнее найти ошибку, когда вы работаете со многими непроверенными функциями одновременно.
Хорошо, теперь, когда мы разобрались с этим, вот несколько опций отладчика. Теперь мы покажем примеры с использованием простого цикла for. Ознакомьтесь с документацией MDN, если вам нужно обновление.
Вывод на консоль
Вы можете проверить свои предположения по всему коду, используя инструкции console.log()
.
Допустим, у нас есть функция, предназначенная для получения массива учащихся и возврата нового массива, содержащего только учащихся, имена которых начинаются с ‘A’.
const allStudents = ["AndreyEx", "Max", "Alex", "Dasha", "Anna", "Dennis"]; function startsWithJ(array) { const aStudents = []; for (let i = 0; i < array.length; i++) { const currentStudent = array[i]; const firstLetter = currentStudent[1]; if (firstLetter === "A") { aStudents.push(currentStudent); } } return aStudents; } startsWithJ(allStudents); // => []
Когда мы запускаем нашу функцию с переданным массивом allStudents
, она возвращает пустой массив. Это не то, чего мы ожидали – мы хотели, чтобы он возвращался ['AndreyEx', 'Alex', 'Anna']
. Возможно, вы быстро поймете, где мы ошиблись, а возможно, и нет (и это нормально!). В любом случае, потерпите немного, пока мы добавим инструкции console.log()
для отладки этой функции.
Одно из наших предположений относительно этого кода заключается в том, что он корректно обращается к каждому элементу в массиве allStudents
. Мы можем использовать console.log()
для печати имени каждого ученика в нашем исходном массиве, вот так:
const allStudents = ["AndreyEx", "Max", "Alex", "Dasha", "Anna", "Dennis"]; function startsWithJ(array) { const aStudents = []; for (let i = 0; i < array.length; i++) { const currentStudent = array[i]; console.log(currentStudent); //ДОБАВЛЕНА СТРОКА const firstLetter = currentStudent[1]; if (firstLetter === "A") { aStudents.push(currentStudent); } } return aStudents; } startsWithJ(allStudents); // LOG: AndreyEx // Max // Alex // Dasha // Anna // Dennis // => []
Имя каждого учащегося корректно выводится на консоль, поэтому мы знаем, что наш цикл for настроен правильно. Давайте проверим другое предположение – что мы правильно проверяем первую букву имени каждого учащегося. Мы можем “видеть, о чем думает машина”, когда получаем доступ к первой букве, печатая нашу переменную firstLetter
.
const allStudents = ["AndreyEx", "Max", "Alex", "Dasha", "Anna", "Dennis"]; function startsWithJ(array) { const aStudents = []; for (let i = 0; i < array.length; i++) { const currentStudent = array[i]; const firstLetter = currentStudent[1]; console.log(firstLetter); //ДОБАВЛЕНА СТРОКА if (firstLetter === "A") { aStudents.push(currentStudent); } } return aStudents; } startsWithJ(allStudents); // LOG: n // a // l // a // n // e // => []
Здесь мы видим, что наше предположение было неверным. Мы случайно проверяем вторую букву каждого имени, и поэтому firstLetter === 'A'
это никогда не соответствует действительности. Нам нужно изменить firstLetter
назначение на currentStudent[0]
вместо currentStudent[1]
. Вот наш окончательный результат:
const allStudents = ["AndreyEx", "Max", "Alex", "Dasha", "Anna", "Dennis"]; function startsWithJ(array) { const aStudents = []; for (let i = 0; i < array.length; i++) { const currentStudent = array[i]; const firstLetter = currentStudent[0]; if (firstLetter === "A") { aStudents.push(currentStudent); } } return aStudents; } startsWithJ(allStudents); // => [ 'AndreyEx', 'Alex', 'Anna' ]
Хотя это довольно простой пример, использование console.log()
для просмотра значений переменных, вызовов функций и т.д. Является допустимым способом отслеживания более сложного кода.
Отладчик узлов
Теперь мы переходим к инструментам отладки, которые позволяют нам “приостанавливать” наш код и внимательнее смотреть на то, что происходит на каждом шаге. Мы начнем с того, который предоставляется Node для терминала (убедитесь, что Node установлен!).
Сначала используйте свой терминал для перехода к каталогу, в котором хранится ваш код. Мы будем использовать ту же функцию, что и выше, но на этот раз с другой ошибкой.
const allStudents = ["AndreyEx", "Max", "Alex", "Dasha", "Anna", "Dennis"]; function startsWithJ(array) { const aStudents = []; for (let i = 0; i <= array.length; i++) { const currentStudent = array[i]; const firstLetter = currentStudent[0]; if (firstLetter === "A") { aStudents.push(currentStudent); } } return aStudents; } startsWithJ(allStudents);
Когда мы запускаем это, мы получаем следующую ошибку:
const firstLetter = currentStudent[0]; TypeError: Cannot read properties of undefined (reading '0')
Аналогично использованию console.log()
, мы выберем место в коде, куда мы хотим заглянуть внутрь и посмотреть, что происходит. В этом случае мы будем использовать debugger
ключевое слово. Поскольку мы видим, что один из параметров currentStudent
не определен, давайте поместим инструкцию debugger рядом с объявленной переменной currentStudent
.
function startsWithJ(array) { const aStudents = []; for (let i = 0; i <= array.length; i++) { const currentStudent = array[i]; debugger; //ДОБАВЛЕНА СТРОКА const firstLetter = currentStudent[0]; if (firstLetter === "A") { aStudents.push(currentStudent); } } return aStudents; }
Оттуда вы можете запустить отладчик с помощью команды node inspect index.js
в вашем терминале (используйте имя вашего файла вместо index.js).
Теперь запустите cont
по приглашению debug
, чтобы запустить цикл. Он остановится в вашей точке останова debugger
. Теперь мы видим нечто подобное в нашем терминале:
break in index.js:7 5 for (let i = 0; i <= array.length; i++) { 6 const currentStudent = array[i]; > 7 debugger; 8 const firstLetter = currentStudent[0]; 9 if (firstLetter === "A") { debug>
В приглашении debug
давайте откроем REPL, выполнив команду repl
. Оттуда мы можем проверить значения переменных, введя их имена в терминале. Мы начнем с проверки значения i
и currentStudent
.
debug> repl Press Ctrl+C to leave debug repl > i 0 > currentStudent 'AndreyEx'
В этом есть смысл! Мы находимся на нашей первой итерации цикла for, где i
должно быть 0 и currentStudent
должно быть ‘AndreyEx’ (первый ученик в нашем массиве allStudents
). Пока все идет хорошо. Чтобы продолжить пошаговое выполнение кода к нашей следующей итерации, нажмите Ctrl + C для выхода из REPL и снова введите cont
в приглашении debug
.
Теперь, когда мы снова входим в REPL, мы должны быть на второй итерации нашего цикла for. Мы можем подтвердить это, снова проверив значения i
и currentStudent
.
debug> repl Press Ctrl+C to leave debug repl > i 1 > currentStudent 'Max'
Это тоже выглядит правильно. Мы собираемся быстро перейти к 6-й итерации нашего цикла for, где currentStudent
должно быть ‘Dennis’.
debug> repl Press Ctrl+C to leave debug repl > i 5 > currentStudent 'Dennis'
Все i
и currentStudent
были такими, как ожидалось, на каждой итерации. Мы все еще не достигли undefined
currentStudent
. Если мы снова пройдемся по коду, мы бы ожидали, что функция завершится, когда мы дойдем до последнего пользователя в нашем массиве allStudents
.
debug> cont break in index.js:7 5 for (let i = 0; i <= array.length; i++) { 6 const currentStudent = array[i]; > 7 debugger; 8 const firstLetter = currentStudent[0]; 9 if (firstLetter === "A") { debug> repl Press Ctrl+C to leave debug repl > i 6 > currentStudent undefined
Но это не то, что произошло. Здесь отладчик узла показал нам, что в нашем цикле for осталась еще одна итерация, из-за чего i
of 6
вышел за пределы. Вот почему currentStudent[0]
было не определено – потому что нет currentStudent
индекса равного 6
. Мы можем исправить наш код, удалив =
из нашего условия цикла for .
const allStudents = ["AndreyEx", "Max", "Alex", "Dasha", "Anna", "Dennis"]; function startsWithJ(array) { const aStudents = []; for (let i = 0; i < array.length; i++) { const currentStudent = array[i]; const firstLetter = currentStudent[0]; if (firstLetter === "A") { aStudents.push(currentStudent); } } return aStudents; } startsWithJ(allStudents); // => [ 'AndreyEx', 'Alex', 'Anna' ]
Отладчик узлов – отличный инструмент для проверки и отладки вашего кода, особенно если вы не хотите усложнять свой код инструкциями console.log()
.
Отладчик Chrome Developer Tools
Инструменты разработчика Chrome предлагают другой инструмент отладки, который вы можете использовать, если к вашему файлу JavaScript подключен HTML-файл. Вы можете создать базовый HTML-файл, единственной целью которого является вложение вашего JS-файла. Мы прикрепили файл JS, включив следующую строку в свой HTML-файл:
<script type="text/javascript" src="index.js"></script>.
В этом примере мы будем использовать тот же код, что и выше, с немного другой ошибкой.
const allStudents = ["AndreyEx", "Max", "Alex", "Dasha", "Anna", "Dennis"]; function startsWithJ(array) { {const aStudents = []; for (let i = 0; i < array.length; i++) { const currentStudent = array[i]; const firstLetter = currentStudent[0]; if (firstLetter === "A") { aStudents.push(currentStudent); } } } return aStudents; } startsWithJ(allStudents);
Мы собираемся открыть HTML-страницу в Chrome, затем получить доступ к инструментам разработчика, нажав F12.
У Dev tools есть своя консоль, которая сразу показывает нам ошибку: Uncaught ReferenceError: jStudents is not defined
. Это происходит в конце нашего кода, куда мы пытаемся вернуться jStudents
. Точно так же, как и в случае с инструментом отладки узла, я собираюсь поместить в свой код инструкцию debugger
, где мы хотим сделать паузу и рассмотреть поближе. Мы поместим ее в начало моей функции.
function startsWithJ(array) { {const aStudents = []; debugger; //ДОБАВЛЕНА СТРОКА for (let i = 0; i < array.length; i++) { const currentStudent = array[i]; const firstLetter = currentStudent[0]; if (firstLetter === "A") { aStudents.push(currentStudent); } } } return aStudents; }
Теперь, когда инструменты разработки все еще открыты, мы собираемся обновить страницу. Если страница еще не открыта, перейдите на панель исходных текстов, чтобы просмотреть свой код. Отладчик должен был приостановить код в вашей инструкции debug.
Если вы посмотрите направо, вы должны увидеть значения ваших переменных и информацию в стеке вызовов. Мы можем заглянуть сюда, чтобы увидеть, как меняются значения наших переменных (например aStudent
) по мере выполнения кода. Вместо ввода cont
в командной строке debug
терминала, приятный пользовательский интерфейс инструмента отладки Chrome позволяет нам нажимать кнопки для пошагового выполнения кода.
Вы должны видеть значения currentStudent
, firstLetter
, i
и aStudent
, которые будут изменяться по мере прохождения итераций.
Быстрая переадресация к последней итерации, aStudents
назначена правильно ['AndreyEx', 'Alex', 'Anna']
. Мы знаем, что наш цикл for работает должным образом. Но когда мы продолжим пошаговое выполнение каждой строки и выйдем из цикла for, вы заметите, что aStudents
это исчезло с нашей вкладки scope. aStudents
ранее была указана как переменная уровня блока, что предполагает, что она могла быть случайно объявлена в области видимости, к которой у нас больше нет доступа.
При более внимательном рассмотрении нашего кода мы можем видеть, что наш оператор return находится за пределами блока кода ({}
), где был объявлен aStudents. Чтобы решить нашу проблему, мы можем поместить объявление переменной и оператор return в одну область видимости, удалив ненужное {}
.
function startsWithJ(array) { const aStudents = []; debugger; //ДОБАВЛЕНА СТРОКА for (let i = 0; i < array.length; i++) { const currentStudent = array[i]; const firstLetter = currentStudent[0]; if (firstLetter === "A") { aStudents.push(currentStudent); } } return aStudents; } startsWithJ(allStudents); // => [ 'Alex', 'Anna', 'Dennis' ]
Подведение итогов
Отладчик Chrome – наш личный фаворит из этих опций. Мы ценим, что нам не нужно вычищать кучу console.log()
, когда мы закончим, и мы думаем, что это намного более интуитивно понятно в использовании, чем отладчик Node. Любой из этих способов допустим для отладки вашего кода. Возможно, вы даже захотите изучить возможность использования отладчика, встроенного в вашу среду разработки IDE.
Теперь у вас есть инструменты для устранения ваших ошибок и тщательного изучения вашего кода. Спасибо за чтение и удачи!