$ sudo teach IT
РАЗДЕЛ 7 · УРОК 1

Колбэки и функции высшего порядка

В JavaScript функции — это не просто «команды». Это полноценные значения: их можно сохранять в переменные, передавать в другие функции и даже возвращать из функций. Именно эта идея лежит в основе половины всего JS-кода.

⏱ ~25 минут 🎓 Для новичков 📦 JavaScript

🧩 Функция — это значение, как число или строка

Когда вы только начинаете учить 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 — везде колбэки или промисы (которые тоже построены на колбэках).

🖱️ События DOM

Клики, нажатия клавиш, скролл, 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 вопросов

Функция высшего порядка с колбэком

Напишите функцию processArray, которая принимает массив чисел и функцию-колбэк. Она должна пройти по всем элементам массива, применить к каждому колбэк и вернуть новый массив с результатами. Затем напишите колбэк square (возводит число в квадрат) и используйте их вместе.

Фабрика приветствий

Напишите функцию makeGreeter, которая принимает строку greeting и возвращает новую функцию. Возвращённая функция принимает имя name и возвращает строку вида 'greeting, name!'. Используйте замыкание для сохранения greeting.