О, нет, ошибки! И как с ними справляться
Транскрипт урока
У вас вечеринка, а закуска кончилась, вы просите подругу что-нибудь купить.

— Эй, можешь сходить в магазин и купить какой-нибудь еды?
Она отвечает:
— Что именно?
Вы говорите:
— Ну, типа чипсы или что-нибудь такое. У нас всё закончилось.
Она отвечает:
— Сколько упаковок чипсов взять?
И у вас уже начинает немного вскипать мозг:
— Да, я не знаю, штук 5.
— А какие чипсы?

И вы так глазами делаете и отвечаете "ааа… забудь", или решаете идти до конца и объясняете подробно задачу: "Возьми 5 средних пакетов картофельных чипсов со вкусом бекона".

Десять минут спустя она возвращается с пустыми руками и говорит "у них не было таких чипсов в пакетах среднего размера".

О "программистах" есть определённые стереотипы и то, что они могут быть слишком конкретными и чересчур дотошными — один из них. Многие думают, что такие люди хорошо разбираются в математике или что-то подобное.

В реальности всё намного сложнее. Не существует всего двух типов людей, спектр типов личности буквально бесконечен. И для некоторых людей программирование немного более органично, потому что компьютеры абсолютно конкретные и абсурдно однозначные. Это совершенно не значит, что если вы считаете, что у вас "нематематический склад ума", вы не сможете стать хорошим разработчиком. Это значит только, что вам нужно научиться лучше распознавать и понимать, как именно работают компьютеры.

Такой способ поведения компьютеров ведёт ко множеству ошибок. Если вы наберёте console.log — console запятая log, вместо console точка log, JavaScript скажет "Понятия не имею, что ты имел в виду".

Вы будете делать ошибки и ваши программы будут содержать ошибки. Жизнь — она такая. Любой программист делает ошибки и это не имеет особого значения. Значение имеет только то, как вы потом с ними справляетесь.

Исправление ошибок — важный навык. Это делает программирование непохожим на другие типы работ: ошибки неизбежны, вы не можете от них полностью застраховаться, а исправление ошибок — часть вашей работы.
Ошибка вроде "запятая вместо точки" это тип, который проще всего заметить и исправить. Это "синтаксическая ошибка", потому что неверный символ, как запятая в этом случае, нарушает синтаксические правила языка.

Когда вы запускаете код с такой ошибкой, интерпретатор JavaScript — та штука, которая исполняет JavaScript-программы — пожалуется: SyntaxError и укажет вам на то место, где по его мнению есть проблема.

const cube = (num) => {
  return num * num * num;
})

→ node test.js
/Users/rakhim/test.js:3
})
 ^
SyntaxError: Unexpected token )
    at Object.exports.runInThisContext (vm.js:78:16)
    at Module._compile (module.js:543:28)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.runMain (module.js:605:10)
    at run (bootstrap_node.js:420:7)
    at startup (bootstrap_node.js:139:9)
    at bootstrap_node.js:535:3
Вот, например, это определение функции и в конце — лишняя скобка. Её там быть не должно и это ломает всю программу, поэтому JavaScript жалуется: "SyntaxError:" Unexpected token (символ))". Эта скобка unexpected — неожиданная.
Синтаксическая ошибка — это как если кто-то бредит вслух. Никто вокруг ничего не понимает.
Следующий тип ошибки подобен синтаксической, но в этом случае вы нарушаете не законы синтаксиса языка, а как бы законы синтаксиса собственного кода. В прошлый раз мы создали функцию abs, которая возвращала абсолютное значение числа.

→ node test.js
/Users/rakhim/test.js:1
 ads(12);
 ^
ReferenceError: ads is not defined
    at Object.<anonymous> (/Users/rakhim/test.js:1:63)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.runMain (module.js:605:10)
    at run (bootstrap_node.js:420:7)
    at startup (bootstrap_node.js:139:9)
    at bootstrap_node.js:535:3
Если вы вызовете ads вместо abs, интерпретатор JavaScript пожалуется: ReferenceError: ads is not defined. Вы использовали, как вам кажется, существующее название, но на самом деле такого названия нет.

Несколько строк, которые следуют после указания ошибки, могут смутить и оттолкнуть вас, но они тут только для пользы. Это stack trace — последовательность вызовов функций, которая привела к ошибке. Когда вы запускаете свою программу, даже крошечную, она становится частью чего-то более крупного — сложной системой JavaScript-кода, который приводится в действие, чтобы оживить вашу программу. Тут видно, что проблема была в моём файле. Следующая строка — это место, откуда был вызван мой код, третья строка — откуда была вызвана вторая и в таком духе можно продолжать дальше. Это как отслеживать шаги в обратном направлении — проблема есть, и мы можем возвращаться по одному шагу назад и смотреть, не нашлась ли ошибка.

Мы допускаем, что вся внутренняя система исправно работает, поэтому ошибка в нашем коде. Когда одна из ваших функций вызывает другую, вы увидите эту последовательность вызовов в стектрейсе.

ReferenceError может случиться с другими константами: например, если ваш код содержит 10 * pi, а pi не существует, потому что вы не создавали константу с точно таким названием, вы получите ReferenceError.
ReferenceError — это как называть кого-то чужим именем.
Следующий тип ошибки — когда вы путаете одну вещь с другой. Взгляните на этот код:

const length = 12;
const num = length(54);
Сначала мы создали константу. Помните, что это как давать чему-то название: в нашем случае — числу 12 даётся название length. В следующей строке мы вызываем функцию length и передаём ей аргумент — число 54. Но подождите! length — это не функция! Это всего лишь число. Числа — это не функции, не ящики, которые производят какие-то действия. И JavaScript пожалуется именно на это:

→ node test.js
/Users/rakhim/test.js:2
const num = length(-54);
            ^

TypeError: length is not a function
    at Object.<anonymous> (/Users/rakhim/test.js:2:13)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.runMain (module.js:605:10)
    at run (bootstrap_node.js:420:7)
    at startup (bootstrap_node.js:139:9)
    at bootstrap_node.js:535:3
Это Ошибка типизации: тип объекта, который вы использовали, неверный. Интерпретатор JavaScript не скажет чем что-то является, но точно скажет чем оно не является. length — это не функция.
Ошибка типизации — это как просить кошку постирать бельё. Возможно, вы хотели попросить об этом вашего друга.
Все эти ошибки — syntax error, reference error и type error — возникают из-за использования неправильных слов. И все они предельно очевидные: вы видите сообщение об ошибке и достаточно хорошо понимаете в чём проблема. Обычно сразу понятно, как их исправить:
  1. Синтаксическая ошибка? Заменить, удалить или добавить символы. Часто проблема в скобках и кавычках: открытые скобки и открытые кавычки должны быть закрыты.
  2. Reference error? Проверить, существует ли тот объект, на который вы ссылаетесь. Возможно, вы использовали неправильное название или забыли создать его.
  3. Ошибка типизации? Убедиться, что вы используете объекты верно. Часто проблема — простая путаница: вы создали и числовую константу и функцию, а потом пытаетесь вызвать число. Наверное, вы хотели вызвать функцию.
Последний тип ошибки, о котором мы сегодня поговорим — самый злой: Логическая ошибка. Допустим, мы пишем функцию, которая конвертирует градусы по фаренгейту (F) в градусы по цельсию (C). Чтобы сконвертировать температуру по одной шкале в другую, нужно вычесть 32 и умножить на 5/9. Например (50°F - 32) x 5/9 = 10°C.

const fahrToCelsius = (fahr) => {
  return fahr - 32 * 5/9;
}
Выглядит нормально? Давайте запустим эту функцию, сконвертируем 50 градусов и выведем на экран:

console.log(fahrToCelsius(50));
И у нас получилось 32.22222222222222. Не 10. Что произошло? JavaScript не пожаловался, когда код запустился, никаких ошибок не выскакивало.

Компьютер не знает, что нам нужно, поэтому он производит вычисление, как мы его и просили. Но такое вычисление ошибочно — допустили ошибку мы. Нам нужно вначале вычесть 32, а потом умножить это на 5/9. Но мы не использовали скобки, поэтому 32 вначале умножилось на 5/9, а затем результат был отнят от температуры в фаренгейтах.

Это Логическая ошибка. Мы не нарушили никаких правил, мы просто сделали что-то не то. Наш пример был простым: мы написали функцию, запустили её и увидели неверный результат. Но представьте, что функция — это только маленькая частица крупной системы. Приложение для формирования бюджета в огромной организации отправляет отчёт в главную бухгалтерию, что в следующем месяце для оплаты счёта за электричество требуются дополнительные $300 000. Организуется экстренное собрание, увольняют людей, генеральный директор снова уходит в запой. Что случилось?

Иногда обнаружить проблему может оказаться трудной задачей: система кондиционирования ожидает, что январская температура будет 32 градуса по цельсию, вместо 10, потому что кто-то забыл использовать скобки в функции.

Борьба с логическими ошибками — это целиком ваша ответственность. И временами — тяжёлая работа, но в конце приходит сильное облегчение и удовлетворение: а-ааа, так вот в чём была проблема!

Теперь ваша очередь делать ошибки! Выполните тест и упражнение, чтобы ощутить боль.
Дополнение к уроку
Листочек
Самый действенный способ понять, как работает участок кода — это расписать его выполнение на бумажке, как если бы вы были компьютером (медленным и немного голодным).
Метод утенка
Метод утёнка — психологический метод решения задачи, делегирующий её мысленному помощнику. Метод описан в книге «Программист-прагматик».
Тестируемый ставит на рабочем столе игрушечного утёнка (или представляет его мысленно; на самом деле уточка — это условно, предмет может быть любым), и когда у него возникает вопрос, на который трудно ответить, то он задаёт его игрушке, как живому человеку, словно она действительно может ответить.

Считается, что правильная формулировка вопроса содержит как минимум половину ответа, а также это дает толчок мыслям, направляя их в нужное русло.

Метод также используется при отладке. Если определённая часть программы не работает, программист пытается объяснить утёнку, что делает каждая строка программы, и в процессе этого сам находит ошибку.
Выводы
  1. Syntax error. Неверное использование языка. Часто лишние или недостающие скобки или кавычки. Что делать? Заменить, удалить или добавить символы. Часто проблема в скобках или кавычках: открытые скобки должны быть закрыты, открытые кавычки должны быть закрыты.
  2. Reference error. Использование несуществующего названия. Что делать? Проверить, существует ли то, на что вы ссылаетесь. Возможно вы использовали ошибочное название или забыли его создать.
  3. Type error. Использование неверного типа, например попытка вызвать константу числа, как функцию. Что делать? Убедиться, что всё используется верно. Часто проблема в простой путанице: вы создали численную константу и функциональную константу, а потом пытаетесь вызвать число. Вероятно вы собирались вызвать функцию.
  4. Logic error. (Логическая ошибка) Ваш код выполняет не то, что требуется, но программа запускается и не выдаёт ошибок трёх перечисленных выше типов. Сломана логика. Что делать? Проверить свой код, убедиться, что он выполняет то, что должен.
Тесты
Пройти тест
Я написал программу, запустил ее, и она упала с ошибкой типа ReferenceError. А, все ок, это была простая опечатка. Я ее исправил, но теперь у меня TypeError. Такое вообще возможно?

Неверно
Верно!
Дальше
Проверить
Завершить тест
Рассмотрите такой код:

const a = 10;
const b = 12;
const c = a() + b;

Есть ли тут ошибки? Если да, то какого типа?
Неверно
Неверно
Неверно
Верно!
Дальше
Проверить
Завершить тест
Возможно ли иметь все четыре типа ошибок в одной строке кода?
Верно!
Неверно
Дальше
Проверить
Завершить тест
Рассмотрите такой код:

const func = () => {
   const a = 120.323;
   return 10 * a * b;
};

Есть ли тут ошибки? Если да, то какого типа?
Неверно
Неверно
Неверно
Верно!
Дальше
Проверить
Завершить тест
Программа не может работать при возникновении ReferenceError, но может работать при возникновении TypeError. Правильно?
Неверно
Неверно
Верно!
Дальше
Проверить
Завершить тест
Представьте что мы выполняем скрипт, в котором есть только одна строчка:

  console.log(sum(5, 10));

Что будет выведено на экран?
Неверно
Неверно
Верно!
Дальше
Проверить
Завершить тест
Что нужно делать в любой непонятной ситуации?
Верно!
Неверно
Неверно
Дальше
Проверить
Завершить тест
Как поступить если мы хотим вывести значения на экран только в определенных ситуациях?
Неверно
Верно!
Неверно
Дальше
Проверить
Завершить тест
Пройти еще раз
Пройти еще раз
Пройти еще раз
Пройти еще раз
Упражнение
Обычно вам нужно избегать ошибок, но в этом упражнении вы будете порождать ошибки. Это поможет вам понять, какие типы ошибок возникают в разных ситуациях, и привыкнуть распознавать их в будущем.
Вы будете работать в двух файлах — reference.js и type.js. Ваша задача — породить две ошибки внутри специально созданных функций.

Сделайте так, чтобы код внутри функции reference() порождал ошибку типа ReferenceError.

Вспомните, что ReferenceError возникает, когда используется несуществующий идентификатор (имя). Например, если ваша функция возвращает значение константы, но такой константы не существует. Или в каком-нибудь выражении (фрагмент кода, который вычисляется в значение, как формула) используется неизвестный идентификатор.

Сделайте так, чтобы код внутри функции type() порождал ошибку типа TypeError.

Вспомните, что TypeError возникает, когда вы используете неправильный тип. Например, если вы пытаетесь вызвать численную константу как функцию.

ВАЖНО
Ошибки должны генерироваться внутри функций reference() и type().