Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Функція-обгортка». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Функція-обгортка (wrapper function) це функція, яка загортає іншу функцію або код, щоб додати додаткову поведінку, контроль чи спрощення виклику.** Вона нічого не ламає в оригіналі: викликає його всередині себе, а «зверху» додає логування, валідацію, перевірку прав, обробку помилок, кешування або вимірювання часу. Технічно це higher-order function: приймає функцію і повертає нову функцію з тією самою сигнатурою. ```javascript function withLogging(fn) { return function (...args) { console.log(`call ${fn.name}`, args); return fn(...args); }; } ``` **Ключове:** обгортка змінює поведінку навколо функції, не змінюючи саму функцію, тому один шаблон поведінки перевикористовується для багатьох функцій.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Функція-обгортка (wrapper function) це функція, яка загортає іншу функцію або код, щоб додати додаткову поведінку, контроль або спростити виклик.** Простіше кажучи, це прошарок навколо функції, який нічого не ламає, але щось додає: логування, валідацію, обробку помилок, кешування тощо. ## Теорія ### TL;DR - Обгортка приймає функцію і повертає нову функцію, яка викликає оригінал всередині себе. - Це higher-order function, класичний спосіб додати наскрізну поведінку. - Оригінальну функцію не змінюють, тому її можна використовувати і без обгортки. - Типові задачі: логування, метрики, try/catch, перевірка прав, кеш, throttle. - Той самий патерн лежить в основі декораторів та middleware. ### Швидкий приклад ```javascript 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'); ``` Вивід: ```text Виклик функції greet з аргументами: [ 'Maria' ] Результат: Привіт, Maria! ``` Тут `withLogging` це функція-обгортка, яка додає «зверху» звичайної функції `greet` додаткову поведінку. ### Основна ідея Ви берете вже наявну функцію або операцію і створюєте нову, яка викликає її всередині себе, але з чимось додатковим навколо виклику. Наприклад: - додати логування перед викликом; - виміряти час виконання; - перевірити аргументи; - обробити помилки через `try/catch`. Щоб обгортка залишалася прозорою, вона має приймати `...args`, повертати результат оригіналу і, якщо потрібен коректний `this`, викликати функцію через `fn.apply(this, args)`. ### Обробка помилок ```javascript 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(); // Помилка під час виклику функції: Щось пішло не так! ``` Тепер навіть якщо внутрішня функція падає, код навколо неї не ламається. ### Вимірювання часу виконання ```javascript 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. ```javascript 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(); ``` ### Обгортка як декоратор: перевірка прав ```javascript 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 нечитабельним і ховають справжнє місце помилки.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.