Колбек-функції
Функції не відрізняються від чисел, рядків або масивів - це просто спеціальний тип даних (об'єкт вищого порядку), значення, яке можна зберігати у змінній або передавати у якості аргументу в іншу функцію.
function greet(name) {
return `Ласкаво просимо ${name}.`;
}
// Викликаємо функцію greet і виводимо результат у консоль
console.log(greet("Манго")); // Ласкаво просимо Манго.
// Виводимо функцію greet у консоль, не викликаючи її
console.log(greet); // ƒ greet() { return `Ласкаво просимо ${name}.`; }
У першому лозі ми викликаємо функцію greet
за допомогою круглих дужок, і в
консоль виводиться результат її виконання. У другому лозі передається
посилання на функцію, а не результат виклику (відсутні круглі дужки), тому в
консоль виводиться її тіло. Це означає, що функцію можна присвоїти у змінну або
передати як аргумент іншої функції.
Функція зворотного виклику (callback, колбек) - це функція, яка передається іншій функції як аргумент, а та, в свою чергу, викликає передану функцію.
Функція вищого порядку (higher order function) - функція, яка приймає у якості параметрів інші функції або повертає функцію у якості результату.
// Колбек-функція
function greet(name) {
console.log(`Ласкаво просимо ${name}.`);
}
// Функція вищого порядку
function registerGuest(name, callback) {
console.log(`Реєструємо гостя ${name}.`);
callback(name);
}
registerGuest("Манго", greet);
Ми передали посилання на функцію greet
як аргумент, тому вона буде присвоєна в
параметр callback
і викликана всередині функції registerGuest
за допомогою
круглих дужок. Ім'я параметра для колбека може бути довільним, головне
пам'ятати, що значенням буде функція.
Інлайн колбеки
Якщо колбек-функція - маленька, і потрібна тільки для передачі аргументом, її можна оголосити безпосередньо на момент виклику функції, в яку передаємо колбек. Така функція буде доступна тільки у якості значення параметра і більше ніде в коді.
function registerGuest(name, callback) {
console.log(`Реєструємо гостя ${name}.`);
callback(name);
}
// Передаємо інлайн функцію greet у якості колбека
registerGuest("Манго", function greet(name) {
console.log(`Ласкаво просимо ${name}.`);
});
// Передаємо інлайн функцію notify у якості колбека
registerGuest("Полі", function notify(name) {
console.log(`Шановний(а) ${name}, ваш номер буде готовий за 30 хвилин.`);
});
Декілька колбеків
Функція може приймати будь-яку кількість колбеків. Наприклад, уявімо, що ми пишемо логіку прийняття дзвінків для телефону. Програма повинна увімкнути автовідповідач, якщо абонент - недоступний, або з'єднати дзвінок в іншому випадку. Доступність абонента будемо імітувати генератором випадкового числа, щоб між різними викликами функції можна було отримати різні результати.
function processCall(recipient) {
// Імітуємо доступність абонента випадковим числом
const isRecipientAvailable = Math.random() > 0.5;
if (!isRecipientAvailable) {
console.log(`Абонент ${recipient} недоступний, залиште повідомлення.`);
// Логіка активації автовідповідача
} else {
console.log(`З'єднуємо з ${recipient}, очікуйте...`);
// Логіка прийняття дзвінка
}
}
processCall("Манго");
Проблема такого підходу полягає у тому, що функція processCall
робить занадто
багато і прив'язує перевірку доступності абонента до двох заздалегідь визначених
дій. Що буде, якщо в майбутньому, замість автовідповідача, потрібно буде
залишати голограму?
Ми могли б написати функцію таким чином, щоб вона повертала якесь значення, і потім за результатом її виконання, робити перевірки і виконувати потрібний код. Але перевірки не стосуються зовнішнього коду і будуть його засмічувати.
Виконаємо рефакторинг функції таким чином, щоб вона приймала два колбеки
onAvailable
і onNotAvailable
, і викликала їх за умовою.
function processCall(recipient, onAvailable, onNotAvailable) {
// Імітуємо доступність абонента випадковим числом
const isRecipientAvailable = Math.random() > 0.5;
if (!isRecipientAvailable) {
onNotAvailable(recipient);
return;
}
onAvailable(recipient);
}
function takeCall(name) {
console.log(`З'єднуємо з ${name}, очікуйте...`);
// Логіка прийняття дзвінка
}
function activateAnsweringMachine(name) {
console.log(`Абонент ${name} недоступний, залиште повідомлення.`);
// Логіка активації автовідповідача
}
function leaveHoloMessage(name) {
console.log(`Абонент ${name} недоступний, записуємо голограму.`);
// Логіка запису голограми
}
processCall("Манго", takeCall, activateAnsweringMachine);
processCall("Полі", takeCall, leaveHoloMessage);
Колбеки застосовуються для обробки дій користувача на сторінці, на момент обробки запитів на сервер, виконання заздалегідь невідомих функцій тощо. У цьому і полягає їх суть - це функції, призначені для відкладеного виконання.
Абстрагування повторення
Абстракція - приховування деталей реалізації. Дозволяє думати про задачі на вищому (абстрактному) рівні. Функції - це хороший спосіб побудови абстракцій.
Наприклад, скрипт виконує якусь дію певну кількість разів. З цією метою можна
написати цикл for
.
for (let i = 0; i < 10; i += 1) {
console.log(i);
}
Чи можемо ми абстрагувати «робити щось N разів» у якості функції? - так,
напишемо функцію, яка викликає console.log()
N разів.
function repeatLog(n) {
for (let i = 0; i < n; i += 1) {
console.log(i);
}
}
repeatLog(5);
Але що робити, якщо ми хочемо виконати щось, крім логування чисел? Оскільки «робити щось» можна уявити функцією, а функції - це просто значення, ми можемо передати дію як аргумент.
function printValue(value) {
console.log(value);
}
function prettyPrint(value) {
console.log("Logging value: ", value);
}
function repeat(n, action) {
for (let i = 0; i < n; i += 1) {
action(i);
}
}
// Передаємо printValue як callback-функцію
repeat(3, printValue);
// 0
// 1
// 2
// Передаємо prettyPrint як callback-функцію
repeat(3, prettyPrint);
// Logging value: 0
// Logging value: 1
// Logging value: 2