> Как реализовать метод перезагрузки счетчика вызовов функции в декораторе JavaScript (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Яндекс

Стек: JavaScript

> Пример ответа

Короткий ответ

Для реализации сброса счетчика вызовов в декораторе нужно хранить счетчик в замыкании и добавить метод reset на обернутую функцию. Декоратор возвращает новую функцию, у которой счетчик инкрементируется при каждом вызове, а через свойство или метод можно обнулить его. Альтернативно - использовать WeakMap для хранения состояния.

Подробное объяснение

Декоратор счетчика - это функция высшего порядка, которая оборачивает исходную функцию, добавляя логику подсчета вызовов. Счетчик хранится в замыкании декоратора, что обеспечивает его изоляцию. Для реализации сброса нужно предоставить публичный интерфейс - обычно метод reset на самой обернутой функции.

Ключевые моменты:

  • Счетчик - приватная переменная в замыкании, недоступная извне напрямую
  • Метод reset присваивается как свойство обернутой функции (функции - объекты первого класса)
  • При каждом вызове оригинальной функции счетчик увеличивается
  • reset обнуляет счетчик и может также сбрасывать дополнительные метаданные (например, время первого вызова)

Для сохранения контекста this и аргументов используется Function.prototype.apply или spread-оператор. Если декоратор применяется к методам класса, нужно учитывать потерю контекста - использовать descriptor.value или Proxy.

На практике

Типичные сценарии:

  • Мониторинг производительности - подсчет вызовов дорогих операций
  • Ограничение частоты вызовов (rate limiting) с возможностью сброса
  • Отладка - логирование количества вызовов с возможностью обнуления статистики
  • A/B тестирование - сброс счетчика при переключении варианта

Важно: метод reset не должен влиять на поведение оригинальной функции. Если декоратор применяется к нескольким функциям, каждая получает собственный изолированный счетчик.

Пример кода

JAVASCRIPT
function countCalls(fn) {
let counter = 0;
function wrapped(...args) {
counter++;
console.log(`Вызов #${counter}`);
return fn.apply(this, args);
}
wrapped.reset = function() {
counter = 0;
console.log('Счетчик сброшен');
};
wrapped.getCount = function() {
return counter;
};
return wrapped;
}
// Использование
const sayHi = countCalls((name) => `Привет, ${name}!`);
sayHi('Анна'); // Вызов #1
sayHi('Иван'); // Вызов #2
console.log(sayHi.getCount()); // 2
sayHi.reset();
console.log(sayHi.getCount()); // 0

Как отвечать на собеседовании

Начни с объяснения концепции замыкания для хранения состояния. Покажи, что функция в JavaScript - объект, поэтому можно добавлять свойства. Упомяни про сохранение контекста this. Если спросят про классы - скажи, что для методов нужен декоратор через Object.defineProperty или Proxy. Добавь про WeakMap для нескольких экземпляров.

Что проверяет интервьюер

  • Понимание замыканий и лексического окружения
  • Умение работать с функциями как с объектами
  • Знание контекста выполнения и потери this
  • Способность проектировать чистый API для вспомогательных методов
  • Понимание разницы между декоратором функции и декоратором метода

Типичные ошибки

  • Забывают сохранить контекст this при обертке
  • Используют глобальную переменную вместо замыкания
  • Не учитывают, что reset должен быть свойством именно обернутой функции, а не глобальным
  • Пытаются сбросить счетчик через изменение внешней переменной, нарушая инкапсуляцию
  • Применяют декоратор к методу класса без привязки контекста (теряется this)

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью