Функции обратного вызова в JavaScript
JavaScript

Функции обратного вызова в JavaScript

Функции в JavaScript являются объектами и могут использоваться так же, как объекты: они могут храниться в переменных, передаваться как параметры другим функциям, создаваться внутри функций и возвращаться как результат функции. И поскольку функции — это объекты, мы можем использовать так называемые функции обратного вызова (callback-функции).

Функция обратного вызова — это техника, пришедшая из функционального программирования, активно использующаяся в JavaScript в целом, и в jQuery в частности. Функциональное программирование — это парадигма программирования, которая использует, если говорить простым языком, функции как аргументы.

Что такое функция обратного вызова?

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

Пример использования функций обратного вызова в jQuery:


$('#btn').click(function() {
    alert('Button 1 Clicked');
});

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

Рассмотрим также пример из JavaScript:


var fruits = ['Apple', 'Pear', 'Orange', 'Banana'];

fruits.forEach(function(eachName, index) {
    console.log(index + 1 + '. ' + eachName);
});

В данном примере мы также передали анонимную функцию в качестве параметра в метод forEach.

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

Как работает функция обратного вызова?

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

Когда мы передаем одну функцию другой в качестве параметра — мы фактически передаем ее определение. На этом этапе передаваемая функция не вызывается и не выполняется.

А так как вторая функция имеет определение функции обратного вызова в качестве одного из параметров, она может выполнить обратный вызов в любое время. Это позволяет нам выполнять функции обратного вызова в любой точке содержащих их функций.

Важно понимать, что функция обратного вызова не выполняется немедленно. Точка внутри содержащей функции, в которой вызывается функция обратного вызова как раз и называется «обратным вызовом».

Функции обратного вызова являются замыканиями

Когда мы передаем функцию в качестве параметра другой функции, мы можем вызвать ее в любой момент внутри содержащей функции, как если бы функция обратного вызова была определена внутри содержащей функции. Это значит, что по сути функция обратного вызова является замыканием. Замыкания имеют доступ к области видимости содержащей функции, а значит могут использовать любые переменные, определенные внутри содержащей функции.

Базовые принципы реализации функций обратного вызова

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

Использование именованных и анонимных функций

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

Пример:



/* глобальная переменная (внимание - использование глобальных переменных является антипаттерном) */
var allUserData = [];

// определение функции logStuff для вывода в консоль
function logStuff(userData) { 
    if (typeof userData === 'string') { 
        console.log(userData); 
    } else if (typeof userData === ‘object’) { 
        for (var item in userData) { 
            console.log(item + ‘: ‘ + userData[item]); 
        }
    }
}

// Функция, принимающая два параметра, одним из которых является коллбэк 
function getInput(options, callback) { 
    allUserData.push(options);
    callback(options);
}

// Пример вызова функции getInput с коллбком
getInput({ name: 'Rich', speciality: 'JavaScript' }, logStuff);

Передача параметров в функцию обратного вызова

Так как функция обратного вызова является обычной функцией, мы можем передавать ей параметры. В предыдущем примере мы уже передавали параметр функции обратного вызова.

Также можно передать функции обратного вызова любую переменную из локальной или глобальной области видимости.


// глобальная переменная
var generalLastName = 'Elison';

function getInput(options, callback) { 
    allUserData.push(options);
    callback(generalLastName, options);
}

Проверка коллбэк-функции перед выполнением

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

Давайте добавим необходимые проверки в функцию getInput из предыдущего примера:


function getInput(options, callback) { 
    allUserData.push(options);

    if (typeof callback === 'function') {
        callback(options); 
    }
}

Использование объекта this в функциях обратного вызова

Нужно быть крайне осторожным, когда внутри коллбэк-функции используется объект this. Если эта функция передается в глобальную функцию, то объект this будет указывать на глобальные объект window, иначе на объект this содержащей функции.

Рассмотрим пример:


var userData = {
    id: 094545,
    fullName: 'Unknown',

    setUserName: function(firstName, lastName)  {
        this.fullName = firstName + ' ' + lastName;
    }
}

function getUserInput(firstName, lastName, callback)  {
    callback(firstName, lastName);
}

Вызовем функцию getUserInput, передав ей в качестве функции обратного вызова метод setUserName объекта userData:


getUserInput('John', 'Snow', userData.setUserName);

Мы ожидаем, что значение аттрибута fullName объекта userData изменится на ‘John Snow’, но это не так:


console.log(userData.fullName); // Unknown
console.log(window.fullName); // John Snow

В примере выше мы изменили глобальный объект window. Чтобы сохранить контекст функции обратного вызова, необходимо изменить способ ее вызова внутри содержащей функции, использовав функции call или apply.

Каждая функция в JavaScript имеет два метода call и apply — оба этих метода используются для изменения контекста выполнения функции и передачи ей параметров.

Метод call принимает значение, которое будет использоваться в качестве объета this внутри функции в качестве своего первого параметра, остальными параметрами должны быть параметры вызываемой функции.

Метод apply также в качестве первого параметра принимает контекст выполнения функции, а в качестве второго — массив параметров вызываемой функции.

Рассмотрим, как использование call или apply может решить проблему, описанную выше.


/* Добавим еще один параметр "callbackObj", в котором будет передаваться контекст выполнения функции */
function getUserInput(firstName, lastName, callback, callbackObj) {
    callback.apply(callbackObj, [firstName, lastName]);
}

getUserInput('John', 'Snow', userData.setUserName, userData);
console.log(userData.fullName); // John Snow

Заключение

Мы убедились, что коллбэк-функции — это довольно простой и, в то же время, мощный инструмент. Они широко используются в JavaScript, например, в асинхронном коде, в обработчиках событий, в методах setTimeout и setInterval.

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

Рассылка
Подпишитесь на рассылку и получайте дайджест новостей и статей.
Никакого спама!
Подписаться