Всем привет! Эта статья представляет собой резюме статьи Джейка Арчибальда (инженер-программист в Google) Не используйте функции в качестве обратных вызовов, если они не предназначены для этого », опубликованной 29 января 2021 года.

В статье обсуждается вопрос использования функций в качестве обратных вызовов, которые могут привести к неожиданному поведению / результату, который отличается от наших ожиданий. Давайте посмотрим, как это может произойти. Итак, без лишних слов, давайте приступим 🏃‍♂️

🦺 Безопасное использование

Представим, что мы используем функцию toReadableNumber (преобразует входные числа в удобочитаемую строковую форму) из some-library как функция обратного вызова, имеющая следующую реализацию:

А toReadableNumber имеет следующую реализацию:

Пока все работает отлично, пока разработчики some-library не решат обновить реализацию toReadableNumber и добавить еще один параметр (base) для преобразования число в читаемый с определенным основанием системы счисления со значением по умолчанию 10, чтобы сделать функцию toReadableNumber обратно совместимой со старым использованием.

📌 Корень всего зла

После того, как сопровождающий обновил функцию, и вы ничего не изменили в своей реализации, произойдет следующее:

На самом деле, помимо самого числа, мы также передаем индекс элемента в массиве и сам массив

Используя toReadableNumber в качестве обратного вызова, мы присвоили index параметру base, который повлияет на вывод toReadableNumber.

Разработчики toReadableNumber чувствовали, что они вносят обратно совместимое изменение, но это нарушает наш код, в основном это не ошибка какой-то библиотеки - они никогда не создавали toReadableNumber как обратный вызов array.map, они не ожидали, что какой-то код уже вызывал функцию с тремя аргументами.

🔨 Лучшая практика

Так что безопаснее всего создать свою собственную функцию, предназначенную для работы с array.map И все! Разработчики toReadableNumber теперь могут добавлять параметры, не нарушая наш код.

📃 Пример parseInt

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

- Ммммм, звучит просто, давайте воспользуемся parseInt 😎!

Вроде ЛЕГКО, но если вы использовали parseInt в качестве функции обратного вызова, вы будете шокированы! если вы использовали parseInt в качестве обратного вызова, вы получите [-10, Nan, 2, 6, 12], в то время как мы ожидаем [-10, 0, 10, 20, 30], это потому что parseInt имеет второй параметр, который является основанием системы счисления

🔨 Решение

Лучше вызвать функцию явно, а не передавать ссылку на функцию напрямую.

📏 Правила линтинга

Используя eslint-plugin-unicorn, вы можете добавить Запретить передачу ссылки на функцию непосредственно в методы итератора в свой набор правил eslint.

Спасибо за чтение, увидимся позже 🙏