← Назад к вопросам

В чем разница между Decorator и Function?

2.0 Middle🔥 111 комментариев
#TypeScript#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между Decorator и Function в TypeScript/Node.js

Это отличный вопрос потому что вызывает путаницу у многих разработчиков. Decorators и обычные функции решают разные проблемы и имеют разные подходы к их решению.

Что такое Decorator

Decorator (также известный как Decorator Pattern) — это structural design pattern который позволяет добавлять новое поведение к объектам динамически. В TypeScript это реализуется через специальный синтаксис с символом `@`.

// Пример Decorator в TypeScript

function Logger(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  
  descriptor.value = function(...args: any[]) {
    console.log(`Calling ${propertyKey} with args:`, args);
    const result = originalMethod.apply(this, args);
    console.log(`Method ${propertyKey} returned:`, result);
    return result;
  };
  
  return descriptor;
}

class Calculator {
  @Logger
  add(a: number, b: number): number {
    return a + b;
  }
}

const calc = new Calculator();
calc.add(2, 3);
// Output:
// Calling add with args: [2, 3]
// Method add returned: 5

Decorator работает на уровне метаклассов (metadata), применяется к классам, методам, свойствам, параметрам, и модифицирует их поведение без изменения самого исходного кода.

Что такое обычная Function

Обычная функция (Higher-Order Function) — это функция которая принимает другую функцию как аргумент или возвращает функцию.

// Пример обычной функции (HOF)

function withLogging(fn: (...args: any[]) => any) {
  return function(...args: any[]) {
    console.log(`Calling ${fn.name} with args:`, args);
    const result = fn(...args);
    console.log(`Method ${fn.name} returned:`, result);
    return result;
  };
}

function add(a: number, b: number): number {
  return a + b;
}

const loggedAdd = withLogging(add);
loggedAdd(2, 3);
// Output:
// Calling add with args: [2, 3]
// Method add returned: 5

Основные отличия

КритерийDecoratorFunction
Синтаксис@decoratorName перед классом/методомОбычный вызов функции
Уровень примененияКомпайл-тайм (через reflection metadata)Runtime
ЧитаемостьДекларативный, явный intentИмперативный, может быть многословным
Когда применяетсяОпределено в коде классаЯвно при использовании
複ComposabilityНесколько decorators можно стакироватьНужна явная обёртка каждый раз
КонтекстЕсть доступ к контексту классаНужно передавать явно

Пример 1: Множественные Decorators

function Logger(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function(...args: any[]) {
    console.log(`[LOG] Calling ${propertyKey}`);
    return originalMethod.apply(this, args);
  };
  return descriptor;
}

function Validate(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function(...args: any[]) {
    if (args.some(arg => arg === undefined)) {
      throw new Error('Arguments cannot be undefined');
    }
    return originalMethod.apply(this, args);
  };
  return descriptor;
}

class UserService {
  @Logger
  @Validate
  updateUser(id: string, name: string): boolean {
    console.log(`Updating user ${id} to ${name}`);
    return true;
  }
}

const service = new UserService();
service.updateUser('123', 'John');
// Output:
// [LOG] Calling updateUser
// (Validate проверит аргументы)
// Updating user 123 to John

Пример 2: Decorators для dependency injection в NestJS

// Это очень практичное применение Decorators

import { Injectable, Controller, Get, Param } from '@nestjs/common';

@Injectable()
class UserService {
  async getUser(id: string) {
    return { id, name: 'John' };
  }
}

@Controller('users')
class UserController {
  constructor(private userService: UserService) {} // DI через Decorator @Injectable
  
  @Get(':id')
  async getUser(@Param('id') id: string) {
    return this.userService.getUser(id);
  }
}

Это невозможно было бы сделать также просто с обычными функциями.

Пример 3: Express middleware vs Decorator

С использованием обычной функции (Express middleware):

function loggingMiddleware(req: Request, res: Response, next: NextFunction) {
  console.log(`${req.method} ${req.path}`);
  next();
}

app.use(loggingMiddleware);

app.get('/users/:id', (req, res) => {
  res.json({ id: req.params.id });
});

С использованием Decorator (в NestJS):

@UseGuards(AuthGuard)
@UseInterceptors(LoggingInterceptor)
@Controller('users')
class UserController {
  @Get(':id')
  getUser(@Param('id') id: string) {
    return { id };
  }
}

Decorator подход более декларативный и читаемый.

Практическое применение

Когда использовать Decorator:

  • Фреймворки (NestJS, TypeORM, TypeGraphQL)
  • Кросс-срезовые concerns (logging, validation, caching)
  • Dependency injection
  • Metadata/reflection требуется
  • Нужна декларативность и ясность intent
@Cacheable()
@Validate()
@RateLimit(100)
getUserById(id: string) {
  // implementation
}

Когда использовать обычные Functions:

  • Простая логика
  • Нет фреймворка
  • Нужна максимальная compatibility
  • Функциональное программирование
  • composition нужна на runtime
const validatedFn = validate(cacheable(rateLimitedFn));

Важные замечания

1. Decorators требуют включение в tsconfig.json

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

2. Порядок Decorators имеет значение

// Decorators применяются снизу вверх
@A
@B
@C
method() { }

// Порядок применения: C -> B -> A

3. Performance

Decorators применяются на compile-time, поэтому не влияют на runtime performance критичным образом. Но отражение (reflection) всё ещё имеет overhead.

Вывод

Decorators и Functions решают одну и ту же проблему (добавление поведения), но используют разные подходы:

  • Decorators: синтаксический сахар, декларативный, требует фреймворка
  • Functions: функциональный подход, явный, более универсальный

В modern Node.js разработке (особенно с NestJS) Decorators стали стандартом потому что они делают код более читаемым и maintainable. Но понимание обоих подходов важно для правильного выбора инструмента для задачи.

В чем разница между Decorator и Function? | PrepBro