От автора: слово колбэк, один из сложнейших для понимания аспектов JavaScript, часто упоминается в контексте функций и событий. Что такое колбэки, и как они работают?
Вы, скорее всего, уже знакомы с колбэками. К примеру, они часто встречаются в обработчиках событий:
1 2 3 |
button.addEventListener("click", function() { ... какое-то действие }) |
Такая callback-функция анонимна и является замыканием. На колбэки можно ссылаться через выражения функций или оберточные функции.
Зачем нужны callback-функции?
JavaScript – язык программирования, работающий с событиями: код выполняется в порядке возникновения событий, но результат вычисляется не обязательно по порядку в коде. То есть код:
1 2 3 4 5 |
function first() { ...do something that takes a lot of calculation... console.log("First"); } first(); |
Распечатает слово First в консоли после вычислений. Я замедлю эту функцию и создам вторую, которая будет выполняться сразу же:
1 2 3 4 5 6 7 8 9 10 |
function first() { setTimeout(function() { console.log("First"); }, 1000); } function second(){ console.log("Second"); } first(); second(); |
Результат в консоли будет отличаться от ожидаемого:
1 2 |
Second First |
Почему? В время выполнения JS находит функцию first() и исполняет ее. Затем без пауз он переходит на вторую функцию и запускает ее. JS никогда не останавливается и ищет, что ему еще выполнить. Вот почему в современных браузерах он так быстро работает.
Важно отметить, что колбэк – это всего лишь определенный способ объявления функций в JS. Явного типа callback-функций не существует. Колбэки появились в ES5 (предыдущий JS стандарт) и JQuery. На данный момент они считаются нормальной техникой программирования.
Улучшаем колбэк-функции
В случае с первым примером с обработчиком события, мы ждали, пока завершится событие click, и только потом исполняли оставшийся код. Однако если вы делаете что-то одно, намного лучше будет заменить колбэк на именованную функцию:
1 2 3 4 |
function makeMenu() { ... do something ... } button.addEventListener("click", makeMenu); |
Результат будет тот же, но так код становится более абстрактным, а функцию makeMenu теперь можно повторно использовать в других частях скрипта. Возникает одна возможная проблема. Если в функцию добавить параметры, функция может срабатывать мгновенно и без события клика:
1 2 3 4 |
function makeMenu(param1) { console.log("Clicked"); } button.addEventListener("click", makeMenu(param1)); |
Результат в консоли без события клика:
1 |
Clicked |
Другими словами, JS запускает событие без какого-либо действия. Исправить это можно, вернувшись к формальной записи колбэк-функций:
1 2 3 4 |
function makeMenu(param1) { console.log("Clicked"); } button.addEventListener("click", function() { makeMenu(param1) } ); |
Или же для параметров можно использовать .bind():
1 |
button.addEventListener("click", function() { makeMenu.bind(null, param1) } ); |
Первый пример можно сократить с помощью функции-стрелки в ES6:
1 |
button.addEventListener('click', () => makeMenu) |
В следующей статье я более подробно расскажу про функции-стрелки. Также в следующей статье я подробно разберу .bind() и продвинутые возможности колбэк-функций.
У вас может закрасться подозрение, что сцепка событий при помощи нескольких колбэк-функций может сильно усложнить код JS и запутать наследование. Вы правы. На сайте Callback Hell вы найдете парочку хороших советов по работе со сцепкой событий. В будущих статьях мы также будем уделять внимание этой проблеме.
Заключение
По существу, колбэк-функции представляют собой способ контролировать порядок выполнения кода в JS, способ добиться выполнения кода в логическом порядке. Такие функции часто называют асинхронным JS: функция передается в виде параметра и говорит «сделай это в будущем». Насколько в будущем или условия выполнения передаваемой функции описываются в колбэк-функции. В ES6 такой подход используют для промисов.
JS пронизан колбэк-функциями, которые являются неотъемлемой частью языка. window.requestAnimationFrame() – пример колбэк-функции, которая выполняется, как только браузера определяет, что он готов к отрисовке контента на экран, что обеспечивает плавность анимации.
Постоянная работа JS (всегда ждет события и мгновенно реагирует на все) также влияет на производительность, особенно в часто-запускаемых событиях типа прокрутки окна, изменения размера окна и при работе с некоторыми полями форм типа range. Я покажу эти примеры в статье, где мы будем искусственно занижать производительность браузера.
Событийный характер JS частично контролируется такими понятиями, как замыкания, область видимости и контекст, о которых я расскажу в отдельных статьях.
Источник: //thenewcode.com/
Редакция: Команда webformyself.