Функции в JavaScript являются объектами и могут использоваться так же, как объекты: они могут храниться в переменных, передаваться как параметры другим функциям, создаваться внутри функций и возвращаться как результат функции. И поскольку функции — это объекты, мы можем использовать так называемые функции обратного вызова (callback-функции).
Функция обратного вызова — это техника, пришедшая из функционального программирования, активно использующаяся в JavaScript в целом, и в jQuery в частности. Функциональное программирование — это парадигма программирования, которая использует, если говорить простым языком, функции как аргументы.
Что такое функция обратного вызова?
Функция обратного вызова — это функция, которая передается другой функции в качестве параметра и та, в свою очередь, вызывает переданную функцию.
Пример использования функций обратного вызова в jQuery:
В данном примере мы передаем функцию в качестве паремтра методу click, а он, в свою очередь, вызывает переданную ему функцию. Метод коллбэков широко используется в jQuery.
Рассмотрим также пример из JavaScript:
В данном примере мы также передали анонимную функцию в качестве параметра в метод forEach.
Перед тем, как рассмотреть более сложные примеры, давайте разберемся, как работают функции обратного вызова.
Как работает функция обратного вызова?
Мы уже говорили, что все функции в JavaScript являются объектами, и именно поэтому мы можем подобно объектам создавать их, передавать в качестве параметров другим функциям, возвращать в качестве результата функции.
Когда мы передаем одну функцию другой в качестве параметра — мы фактически передаем ее определение. На этом этапе передаваемая функция не вызывается и не выполняется.
А так как вторая функция имеет определение функции обратного вызова в качестве одного из параметров, она может выполнить обратный вызов в любое время. Это позволяет нам выполнять функции обратного вызова в любой точке содержащих их функций.
Важно понимать, что функция обратного вызова не выполняется немедленно. Точка внутри содержащей функции, в которой вызывается функция обратного вызова как раз и называется «обратным вызовом».
Функции обратного вызова являются замыканиями
Когда мы передаем функцию в качестве параметра другой функции, мы можем вызвать ее в любой момент внутри содержащей функции, как если бы функция обратного вызова была определена внутри содержащей функции. Это значит, что по сути функция обратного вызова является замыканием. Замыкания имеют доступ к области видимости содержащей функции, а значит могут использовать любые переменные, определенные внутри содержащей функции.
Базовые принципы реализации функций обратного вызова
Функции обратного вызова — это несложный прием, однако есть некоторые основные принципы при их реализации, с которыми мы должны познакомиться прежде, чем начать разрабатывать собственные функции обратного вызова.
Использование именованных и анонимных функций
В приведенных выше примерах мы использовали анонимные функции в качестве функций обратного вызова. Можно также определять именованные функции и передавать в качестве коллбэка имя функции.
Пример:
Передача параметров в функцию обратного вызова
Так как функция обратного вызова является обычной функцией, мы можем передавать ей параметры. В предыдущем примере мы уже передавали параметр функции обратного вызова.
Также можно передать функции обратного вызова любую переменную из локальной или глобальной области видимости.
Проверка коллбэк-функции перед выполнением
Перед вызовом функции обратного вызова всегда необходимо проверять, что она действительно является функцией. Также это позволяет сделать функцию обратного вызова необязательным аргументом.
Давайте добавим необходимые проверки в функцию getInput из предыдущего примера:
Использование объекта this в функциях обратного вызова
Нужно быть крайне осторожным, когда внутри коллбэк-функции используется объект this. Если эта функция передается в глобальную функцию, то объект this будет указывать на глобальные объект window, иначе на объект this содержащей функции.
Рассмотрим пример:
Вызовем функцию getUserInput, передав ей в качестве функции обратного вызова метод setUserName объекта userData:
Мы ожидаем, что значение аттрибута fullName объекта userData изменится на ‘John Snow’, но это не так:
В примере выше мы изменили глобальный объект window. Чтобы сохранить контекст функции обратного вызова, необходимо изменить способ ее вызова внутри содержащей функции, использовав функции call или apply.
Каждая функция в JavaScript имеет два метода call и apply — оба этих метода используются для изменения контекста выполнения функции и передачи ей параметров.
Метод call принимает значение, которое будет использоваться в качестве объета this внутри функции в качестве своего первого параметра, остальными параметрами должны быть параметры вызываемой функции.
Метод apply также в качестве первого параметра принимает контекст выполнения функции, а в качестве второго — массив параметров вызываемой функции.
Рассмотрим, как использование call или apply может решить проблему, описанную выше.
Заключение
Мы убедились, что коллбэк-функции — это довольно простой и, в то же время, мощный инструмент. Они широко используются в JavaScript, например, в асинхронном коде, в обработчиках событий, в методах setTimeout и setInterval.
Теперь мы готовы к тому, чтобы писать собственные функции обратного вызова там, где это необходимо. Это позволит не только повысить уровень абстракции кода и тем самым избежать дублирования, но и сделать код более читаемым и поддерживаемым.