Функція-декоратор
функція-декоратор - це особливий вид функції-обгортки, яка модифікує поведінку іншої функції, не змінюючи її вихідний код.
Це потужний патерн, який часто зустрічається у функціональному програмуванні, Python і сучасних JS-фреймворках (наприклад, NestJS, React decorators, MobX тощо). Розберемо детально.
Визначення
Функція-декоратор - це функція, що приймає іншу функцію і повертає нову, з тим самим інтерфейсом, але розширеною поведінкою.
По суті, це "обгортка з наміром" - вона декорує (прикрашає, додає можливості) вихідну функцію, не змінюючи її напряму.
Базовий приклад
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('Богдан');Виведе:
Перед викликом функції
Привіт, Богдан!
Після виклику функціїТут decorator додав "поведінку до і після", не змінюючи саму sayHello.
Приклад 2 - декоратор для вимірювання часу виконання
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
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 - декоратори у стилі "композиції"
Можна комбінувати декілька декораторів:
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 та ін.):
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.
Це синтаксичний цукор для:
User.prototype.sayHello = Log(User.prototype, 'sayHello', descriptor).value;Декоратори != просто обгортки
| Відмінність | Обгортка | Декоратор |
|---|---|---|
| Мета | Додати логіку навколо функції | Змінити або доповнити поведінку функції |
| Повертає | Зазвичай нову функцію | Теж нову, але з тим самим інтерфейсом |
| Часто використовується | Для захисту (try/catch), логування, throttling | Для анотацій, DI, валідації, кешування |
| Рівень | Будь-яка функція | Часто методи або класи |
| У JS (ES) | Не має спец. синтаксису до ES2022 | Має синтаксис @decorator |
ПІДСУМОК
| Властивість | Опис |
|---|---|
| Що це | Функція, що приймає іншу функцію і повертає змінену |
| Мета | Додати функціональність без зміни вихідного коду |
| Тип | Функція вищого порядку (higher-order function) |
| Типові приклади | Логування, кеш, метрики, безпека, анотації |
| Підтримка в ES | ES7+ (через синтаксис @decorator) |
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.