> Как реализовать метод перезагрузки счетчика вызовов функции в декораторе JavaScript (JavaScript)
Уровень: senior · Роль: frontend · Категория: Технические вопросы
Компании: Яндекс
Стек: JavaScript
> Пример ответа
Короткий ответ
Для реализации сброса счетчика вызовов в декораторе нужно хранить счетчик в замыкании и добавить метод reset на обернутую функцию. Декоратор возвращает новую функцию, у которой счетчик инкрементируется при каждом вызове, а через свойство или метод можно обнулить его. Альтернативно - использовать WeakMap для хранения состояния.
Подробное объяснение
Декоратор счетчика - это функция высшего порядка, которая оборачивает исходную функцию, добавляя логику подсчета вызовов. Счетчик хранится в замыкании декоратора, что обеспечивает его изоляцию. Для реализации сброса нужно предоставить публичный интерфейс - обычно метод reset на самой обернутой функции.
Ключевые моменты:
- Счетчик - приватная переменная в замыкании, недоступная извне напрямую
- Метод
resetприсваивается как свойство обернутой функции (функции - объекты первого класса) - При каждом вызове оригинальной функции счетчик увеличивается
resetобнуляет счетчик и может также сбрасывать дополнительные метаданные (например, время первого вызова)
Для сохранения контекста this и аргументов используется Function.prototype.apply или spread-оператор. Если декоратор применяется к методам класса, нужно учитывать потерю контекста - использовать descriptor.value или Proxy.
На практике
Типичные сценарии:
- Мониторинг производительности - подсчет вызовов дорогих операций
- Ограничение частоты вызовов (rate limiting) с возможностью сброса
- Отладка - логирование количества вызовов с возможностью обнуления статистики
- A/B тестирование - сброс счетчика при переключении варианта
Важно: метод reset не должен влиять на поведение оригинальной функции. Если декоратор применяется к нескольким функциям, каждая получает собственный изолированный счетчик.
Пример кода
JAVASCRIPTfunction 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('Анна'); // Вызов #1sayHi('Иван'); // Вызов #2console.log(sayHi.getCount()); // 2sayHi.reset();console.log(sayHi.getCount()); // 0
Как отвечать на собеседовании
Начни с объяснения концепции замыкания для хранения состояния. Покажи, что функция в JavaScript - объект, поэтому можно добавлять свойства. Упомяни про сохранение контекста this. Если спросят про классы - скажи, что для методов нужен декоратор через Object.defineProperty или Proxy. Добавь про WeakMap для нескольких экземпляров.
Что проверяет интервьюер
- Понимание замыканий и лексического окружения
- Умение работать с функциями как с объектами
- Знание контекста выполнения и потери
this - Способность проектировать чистый API для вспомогательных методов
- Понимание разницы между декоратором функции и декоратором метода
Типичные ошибки
- Забывают сохранить контекст
thisпри обертке - Используют глобальную переменную вместо замыкания
- Не учитывают, что
resetдолжен быть свойством именно обернутой функции, а не глобальным - Пытаются сбросить счетчик через изменение внешней переменной, нарушая инкапсуляцию
- Применяют декоратор к методу класса без привязки контекста (теряется
this)
> Похожие задачи по frontend
Приведите пример разницы this и замыкания в JavaScript
Какие встроенные методы JavaScript помогают соединить строки с разделителем
Как обрабатывать превышение лимита вызовов функции в JavaScript и что возвращать
Как обработать результаты двух асинхронных запросов с условиями success, fail и timeout в JavaScript?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью