Функція-обгортка
Функція-обгортка (wrapper function) це функція, яка загортає іншу функцію або код, щоб додати додаткову поведінку, контроль або спростити виклик. Простіше кажучи, це прошарок навколо функції, який нічого не ламає, але щось додає: логування, валідацію, обробку помилок, кешування тощо.
Теорія
TL;DR
- Обгортка приймає функцію і повертає нову функцію, яка викликає оригінал всередині себе.
- Це higher-order function, класичний спосіб додати наскрізну поведінку.
- Оригінальну функцію не змінюють, тому її можна використовувати і без обгортки.
- Типові задачі: логування, метрики, try/catch, перевірка прав, кеш, throttle.
- Той самий патерн лежить в основі декораторів та middleware.
Швидкий приклад
function greet(name) {
return `Привіт, ${name}!`;
}
function withLogging(fn) {
return function (...args) {
console.log(`Виклик функції ${fn.name} з аргументами:`, args);
const result = fn(...args);
console.log('Результат:', result);
return result;
};
}
const loggedGreet = withLogging(greet);
loggedGreet('Maria');Вивід:
Виклик функції greet з аргументами: [ 'Maria' ]
Результат: Привіт, Maria!Тут withLogging це функція-обгортка, яка додає «зверху» звичайної функції greet додаткову поведінку.
Основна ідея
Ви берете вже наявну функцію або операцію і створюєте нову, яка викликає її всередині себе, але з чимось додатковим навколо виклику. Наприклад:
- додати логування перед викликом;
- виміряти час виконання;
- перевірити аргументи;
- обробити помилки через
try/catch.
Щоб обгортка залишалася прозорою, вона має приймати ...args, повертати результат оригіналу і, якщо потрібен коректний this, викликати функцію через fn.apply(this, args).
Обробка помилок
function riskyOperation() {
throw new Error('Щось пішло не так!');
}
function safeWrapper(fn) {
return function (...args) {
try {
return fn(...args);
} catch (err) {
console.error('Помилка під час виклику функції:', err.message);
return null;
}
};
}
const safeOp = safeWrapper(riskyOperation);
safeOp(); // Помилка під час виклику функції: Щось пішло не так!Тепер навіть якщо внутрішня функція падає, код навколо неї не ламається.
Вимірювання часу виконання
function heavyTask() {
for (let i = 0; i < 1e7; i++) {}
}
function measureTime(fn) {
return function (...args) {
const start = performance.now();
fn(...args);
const end = performance.now();
console.log(`Час виконання: ${(end - start).toFixed(2)}ms`);
};
}
const measuredTask = measureTime(heavyTask);
measuredTask(); // Час виконання: 12.34msАсинхронна обгортка
Для async функції обгортка теж має бути асинхронною, інакше try/catch не спіймає відхилений promise.
async function fetchData() {
const res = await fetch('https://api.example.com/data');
return res.json();
}
function withErrorHandling(fn) {
return async function (...args) {
try {
return await fn(...args);
} catch (e) {
console.error('Помилка запиту:', e);
return null;
}
};
}
const safeFetch = withErrorHandling(fetchData);
safeFetch();Обгортка як декоратор: перевірка прав
function authWrapper(fn) {
return function (user, ...args) {
if (!user.isAdmin) {
throw new Error('Доступ заборонено');
}
return fn(user, ...args);
};
}
function deletePost(user, postId) {
console.log(`Допис ${postId} видалено`);
}
const deletePostWithAuth = authWrapper(deletePost);
deletePostWithAuth({ isAdmin: true }, 42); // Допис 42 видалено
deletePostWithAuth({ isAdmin: false }, 42); // Error: Доступ забороненоНавіщо потрібні функції-обгортки
| Мета | Приклад |
|---|---|
| Розширення функціональності | Додати логування, метрики, кешування |
| Безпека | Перевірити права доступу або токен |
| Обробка помилок | Загорнути ризиковані операції в try/catch |
| Перевикористання | Створити шаблон поведінки для багатьох функцій |
| Функціональне програмування | Використати higher-order functions |
Підсумкова таблиця
| Властивість | Опис |
|---|---|
| Що це | Функція, яка викликає іншу всередині себе |
| Мета | Додати або змінити поведінку без зміни оригіналу |
| Тип | Higher-order function |
| Типові приклади | Логування, кеш, валідація, try/catch, метрики |
| Плюси | Перевикористання, безпека, читабельність |
Типові помилки
- Не повертати результат оригінальної функції: обгортка «з'їдає» значення, і виклик раптом дає
undefined. - Загортати
asyncфункцію синхронною обгорткою: помилка перетворюється на необроблене відхилення promise, аtry/catchмовчить. - Втрачати
this: якщо обгортаєте метод об'єкта, викликайтеfn.apply(this, args), а неfn(...args). - Забувати, що
fn.nameіfn.lengthу нової функції інші, тому код, який читає ім'я чи кількість аргументів, зламається. - Нагромаджувати обгортки без міри: п'ять шарів навколо однієї функції роблять stack trace нечитабельним і ховають справжнє місце помилки.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.