Колбэки и функции высшего порядка
В JavaScript функции — это не просто «команды». Это полноценные значения: их можно сохранять в переменные, передавать в другие функции и даже возвращать из функций. Именно эта идея лежит в основе половины всего JS-кода.
🧩 Функция — это значение, как число или строка
Когда вы только начинаете учить JavaScript, функция кажется чем-то особенным — вы объявляете её с ключевым словом function, даёте ей имя, а потом «вызываете». Как будто это специальная конструкция языка, отдельная от обычных значений.
Но на самом деле в JavaScript функция — это обычное значение. Точно так же, как число 42, строка "привет" или булево true. И раз это значение — с ним можно делать то, что делают со значениями:
Как любое другое значение: const greet = function() {}
Как аргумент: doSomething(greet)
Как результат: return function() {}
Посмотрите на пример — функция сохраняется в переменную точно как любое другое значение:
// Обычная переменная — хранит число
const age = 25;
// Обычная переменная — хранит строку
const name = "Алиса";
// Обычная переменная — хранит ФУНКЦИЮ
const greet = function() {
console.log("Привет, мир!");
};
// Вызываем функцию через переменную
greet(); // → "Привет, мир!"
// Можно присвоить функцию в другую переменную
const sayHello = greet;
sayHello(); // → "Привет, мир!" — работает точно так же!
💡 Ключевая мысль: когда вы пишете greet без скобок — вы получаете саму функцию как значение. Когда пишете greet() со скобками — вы её вызываете. Это различие критически важно для понимания колбэков.
📞 Колбэк: функция как аргумент
Колбэк (callback) — это функция, которую вы передаёте в другую функцию как аргумент. Слово «callback» переводится как «обратный вызов» — то есть функция, которую вызовут «обратно», когда настанет нужный момент.
Звучит страшно? Давайте с аналогией. Представьте, что вы позвонили в пиццерию, сделали заказ и сказали: «Когда пицца будет готова — позвоните мне». Вы дали им свой номер телефона — это и есть «колбэк». Пиццерия сделает своё дело (испечёт пиццу), а потом «вызовет вас обратно» по этому номеру.
В коде это выглядит так: вы передаёте функцию в другую функцию, и та вызовет вашу функцию в нужный момент.
// Простейший пример колбэка
function greet(name) {
console.log("Привет, " + name + "!");
}
function processUser(username, callback) {
// Делаем что-то с пользователем...
console.log("Обрабатываем пользователя: " + username);
// А потом вызываем колбэк
callback(username);
}
// Передаём функцию greet как колбэк
processUser("Алиса", greet);
// → "Обрабатываем пользователя: Алиса"
// → "Привет, Алиса!"
Обратите внимание: когда мы пишем processUser("Алиса", greet), мы передаём greet без скобок. Мы не вызываем функцию прямо сейчас — мы передаём её как значение, чтобы processUser сама решила, когда её вызвать.
Чаще всего колбэк передают прямо «на месте» в виде анонимной функции или стрелочной функции:
// Анонимная функция как колбэк
processUser("Боб", function(name) {
console.log("Добро пожаловать, " + name + "!");
});
// Стрелочная функция как колбэк (современный стиль)
processUser("Карл", (name) => {
console.log("Рады видеть тебя, " + name + "!");
});
🔑 Запомните: колбэк — это просто функция-аргумент. Вы определяете «что делать», а другая функция решает «когда делать». Разделение ответственности — вот в чём смысл.
🏆 Функции высшего порядка (Higher-Order Functions)
Функция высшего порядка — это функция, которая работает с другими функциями: либо принимает функцию как аргумент, либо возвращает функцию как результат (либо и то, и другое).
Название «высшего порядка» пришло из математики. В математике функция высшего порядка — это такая, которая работает не с числами, а с другими функциями. Например, производная — это функция высшего порядка: она принимает функцию и возвращает другую функцию.
Напишем свою функцию высшего порядка с нуля, чтобы понять механику:
// Функция высшего порядка — принимает колбэк
function applyTwice(fn, value) {
return fn(fn(value));
}
function double(x) {
return x * 2;
}
function addTen(x) {
return x + 10;
}
console.log(applyTwice(double, 5)); // → 20 (5*2=10, 10*2=20)
console.log(applyTwice(addTen, 5)); // → 25 (5+10=15, 15+10=25)
// Это мощно: одна функция applyTwice работает с любым колбэком!
Теперь посмотрим на функцию, которая возвращает другую функцию:
// Функция высшего порядка — возвращает новую функцию
function makeMultiplier(factor) {
// Возвращаем функцию, которая умножает на factor
return function(number) {
return number * factor;
};
}
const triple = makeMultiplier(3); // получили функцию "умножить на 3"
const times5 = makeMultiplier(5); // получили функцию "умножить на 5"
console.log(triple(10)); // → 30
console.log(times5(10)); // → 50
console.log(triple(7)); // → 21
// Обе функции "помнят" свой factor — это называется замыкание!
🌟 Важно: функция makeMultiplier — это «фабрика функций». Вы вызываете её один раз с настройкой, и получаете готовую специализированную функцию. Это невероятно полезный паттерн в реальном коде.
🌍 Колбэки в реальном JavaScript
Колбэки — это не абстрактная теория. Они буквально везде в JavaScript. Давайте посмотрим на два самых известных примера.
⏰ setTimeout — отложенный вызов
setTimeout принимает два аргумента: колбэк (что сделать) и время в миллисекундах (когда сделать). Браузер ждёт нужное время, а потом вызывает вашу функцию.
// Через 2000 мс (2 секунды) вызовется колбэк
setTimeout(function() {
console.log("Прошло 2 секунды!");
}, 2000);
// Современный стиль — стрелочная функция
setTimeout(() => {
console.log("Ещё один таймер!");
}, 3000);
console.log("Это выполнится СРАЗУ, не ждёт таймера");
// → "Это выполнится СРАЗУ, не ждёт таймера"
// → (через 2 сек) "Прошло 2 секунды!"
// → (через 3 сек) "Ещё один таймер!"
🖱️ addEventListener — реакция на события
addEventListener принимает название события и колбэк. Браузер сам следит за событием, и когда оно произойдёт — вызовет ваш колбэк. Это и есть «обратный вызов» в чистом виде.
const button = document.getElementById("myButton");
// Передаём колбэк: что делать при клике
button.addEventListener("click", function(event) {
console.log("Кнопка нажата!");
console.log("Клик был в точке:", event.clientX, event.clientY);
});
// Или именованную функцию
function handleClick(event) {
console.log("Обработчик клика сработал");
}
button.addEventListener("click", handleClick);
// handleClick — без скобок! Мы передаём функцию, а не вызываем её
Видите паттерн? В обоих случаях вы говорите: «Вот функция — вызови её, когда настанет время». Это фундаментальная идея асинхронного программирования в JavaScript.
🛠️ Пишем свою функцию высшего порядка
Давайте напишем полноценный, практичный пример. Представьте, что у вас есть список студентов, и нужно применить к каждому какое-то действие. Вместо того чтобы писать разные циклы, создадим одну универсальную функцию:
// Наша функция высшего порядка
function processEach(items, action) {
for (let i = 0; i < items.length; i++) {
action(items[i], i); // вызываем колбэк для каждого элемента
}
}
const students = ["Алиса", "Боб", "Карл", "Диана"];
// Используем разные колбэки для разных задач
processEach(students, function(student, index) {
console.log(`${index + 1}. Привет, ${student}!`);
});
// → "1. Привет, Алиса!"
// → "2. Привет, Боб!"
// → ...
processEach(students, (student) => {
console.log(`Студент ${student} добавлен в список.`);
});
// Одна функция — бесконечные возможности благодаря колбэку!
Теперь напишем фабрику — функцию, которая создаёт приветственные функции для разных языков:
// Фабрика приветствий — возвращает функцию
function makeGreeter(greeting) {
return function(name) {
return `${greeting}, ${name}!`;
};
}
const greetRu = makeGreeter("Привет");
const greetEn = makeGreeter("Hello");
const greetEs = makeGreeter("Hola");
console.log(greetRu("Алиса")); // → "Привет, Алиса!"
console.log(greetEn("Alice")); // → "Hello, Alice!"
console.log(greetEs("Alicia")); // → "Hola, Alicia!"
// Каждая функция "запомнила" своё приветствие — магия замыканий!
✨ Вы только что написали замыкание! Когда функция «запоминает» переменные из внешней области видимости (как greeting внутри возвращённой функции) — это называется замыканием (closure). Это одна из самых мощных концепций JS.
⚡ Почему колбэки — сердце JavaScript
JavaScript с самого начала создавался для браузера. А в браузере всё происходит асинхронно: пользователь кликает когда хочет, данные с сервера приходят когда хотят, анимации тикают по своему расписанию. JavaScript не может «остановиться и подождать» — страница замёрзнет.
Решение — колбэки. Вы говорите браузеру: «Когда придут данные — вызови эту функцию». Браузер продолжает работать, а когда данные появились — честно вызывает ваш колбэк.
fetch, XMLHttpRequest, axios — везде колбэки или промисы (которые тоже построены на колбэках).
Клики, нажатия клавиш, скролл, resize — всё через addEventListener с колбэком.
forEach, map, filter, reduce — всё принимает колбэк. Об этом весь следующий урок!
setTimeout, setInterval — классические примеры отложенных колбэков.
🎯 Главный вывод: понять колбэки и функции высшего порядка — значит понять, как думает JavaScript. Большинство библиотек и фреймворков (React, Node.js, Express) построены именно на этих идеях. Не торопитесь — вернитесь к примерам, запустите их в консоли браузера, поэкспериментируйте.
✅ Итоги урока
- Функция в JS — это значение: её можно сохранять, передавать и возвращать
- Колбэк — функция, переданная как аргумент другой функции
- Колбэк без скобок — это передача функции. Со скобками — вызов прямо сейчас
- Функция высшего порядка — принимает функцию как аргумент или возвращает функцию
- Фабрика функций — HOF, возвращающая новую настроенную функцию (например,
makeMultiplier) setTimeoutиaddEventListener— знакомые примеры HOF из браузерного JS- Колбэки — основа асинхронного JS и всей экосистемы: методы массивов, события, запросы
В следующем уроке применим эти знания к массивам: разберём forEach и map — самые популярные функции высшего порядка в повседневном JS-коде. 🚀
Функции как значения и колбэки
7 вопросов