Всем привет! Эта статья представляет собой резюме статьи Джейка Арчибальда (инженер-программист в 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.
Спасибо за чтение, увидимся позже 🙏