Skip to main content

Функція-декоратор

функція-декоратор - це особливий вид функції-обгортки, яка модифікує поведінку іншої функції, не змінюючи її вихідний код.

Це потужний патерн, який часто зустрічається у функціональному програмуванні, Python і сучасних JS-фреймворках (наприклад, NestJS, React decorators, MobX тощо). Розберемо детально.


Визначення

Функція-декоратор - це функція, що приймає іншу функцію і повертає нову, з тим самим інтерфейсом, але розширеною поведінкою.

По суті, це "обгортка з наміром" - вона декорує (прикрашає, додає можливості) вихідну функцію, не змінюючи її напряму.


Базовий приклад

javascript
function decorator(fn) { return function(...args) { console.log('Перед викликом функції'); const result = fn(...args); console.log('Після виклику функції'); return result; }; } function sayHello(name) { console.log(`Привіт, ${name}!`); } const decorated = decorator(sayHello); decorated('Богдан');

Виведе:

javascript
Перед викликом функції Привіт, Богдан! Після виклику функції

Тут decorator додав "поведінку до і після", не змінюючи саму sayHello.


Приклад 2 - декоратор для вимірювання часу виконання

javascript
function measureTime(fn) { return function(...args) { const start = performance.now(); const result = fn(...args); const end = performance.now(); console.log(`Час виконання: ${(end - start).toFixed(2)}ms`); return result; }; } function heavyCalc(n) { for (let i = 0; i < n; i++) {} } const timedCalc = measureTime(heavyCalc); timedCalc(1e7);

Приклад 3 - декоратор для try/catch

javascript
function safe(fn) { return function(...args) { try { return fn(...args); } catch (err) { console.error('Помилка:', err.message); } }; } function risky() { throw new Error('Щось пішло не так'); } const safeRisky = safe(risky); safeRisky(); // Помилка: Щось пішло не так

Такий декоратор часто використовують, щоб автоматично ловити помилки у десятках функцій, не дублюючи try/catch.


Приклад 4 - декоратори у стилі "композиції"

Можна комбінувати декілька декораторів:

javascript
const log = fn => (...args) => { console.log(`Виклик ${fn.name} з аргументами:`, args); return fn(...args); }; const time = fn => (...args) => { const start = performance.now(); const result = fn(...args); console.log(`${fn.name} виконана за ${performance.now() - start}ms`); return result; }; function sum(a, b) { return a + b; } const decoratedSum = log(time(sum)); decoratedSum(5, 7);

Декоратори можна комбінувати, як фільтри: кожен додає "шар" функціональності.


Приклад 5 - декоратори класів і методів (синтаксис ES7)

JS підтримує декоратори на рівні класів (у NestJS, Angular, MobX та ін.):

javascript
function Log(target, property, descriptor) { const original = descriptor.value; descriptor.value = function(...args) { console.log(`Виклик ${property} з:`, args); return original.apply(this, args); }; return descriptor; } class User { @Log sayHello(name) { console.log(`Привіт, ${name}!`); } } new User().sayHello('Богдан');

Тут @Log автоматично "обгортає" метод sayHello. Це синтаксичний цукор для:

javascript
User.prototype.sayHello = Log(User.prototype, 'sayHello', descriptor).value;

Декоратори != просто обгортки

ВідмінністьОбгорткаДекоратор
МетаДодати логіку навколо функціїЗмінити або доповнити поведінку функції
ПовертаєЗазвичай нову функціюТеж нову, але з тим самим інтерфейсом
Часто використовуєтьсяДля захисту (try/catch), логування, throttlingДля анотацій, DI, валідації, кешування
РівеньБудь-яка функціяЧасто методи або класи
У JS (ES)Не має спец. синтаксису до ES2022Має синтаксис @decorator

ПІДСУМОК

ВластивістьОпис
Що цеФункція, що приймає іншу функцію і повертає змінену
МетаДодати функціональність без зміни вихідного коду
ТипФункція вищого порядку (higher-order function)
Типові прикладиЛогування, кеш, метрики, безпека, анотації
Підтримка в ESES7+ (через синтаксис @decorator)

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.