Как реализовать методы класса EventEmitter и какие аргументы они должны принимать
Что проверяют
Паттерн «наблюдатель» в живом коде: как хранить подписки, какие аргументы принимать и что возвращать. Решение на три метода пишут почти все, поэтому следом просят once и спрашивают, что будет, если слушатель отпишется или бросит исключение прямо во время emit.
Как отвечать
Сигнатуры лучше брать по образцу Node.js, интервьюер узнает их сразу:
on(event, listener): имя события (строка илиSymbol) и функция. Возвращаетthisдля цепочки или, в современном стиле, функцию отписки, которую удобно вызвать в очисткеuseEffect;off(event, listener): та же пара. Удаляет по ссылке, поэтому анонимную функцию, переданную вon, потом не отписать;once(event, listener): оборачивает слушателя, который снимает себя перед вызовом. Обёртке ставятwrapper.listener = listener, чтобыoffс исходной функцией тоже её находил;emit(event, ...args): передаёт аргументы слушателям и возвращаетtrue, если они были. В Node устроено так же.
Хранилище делайте на Map<string, Set<Function>> или на Map с массивами, если нужно разрешить повторную подписку той же функции. В emit перебирайте копию, [...listeners], тогда отписка внутри обработчика не пропустит соседа.
Хорошо упомянуть типизацию: class Emitter<E extends Record<string, unknown[]>> и on<K extends keyof E>(event: K, listener: (...args: E[K]) => void), тогда опечатку в имени события поймает компилятор. И назвать браузерную альтернативу: EventTarget с CustomEvent и отпиской через AbortSignal в опции { signal }.
Где спотыкаются
Хранят подписки в {}. Событие с именем constructor находит метод прототипа, проверка if (!this.events[event]) не срабатывает, и push падает. Нужен Map или Object.create(null).
Не думают об ошибках. Исключение в одном слушателе прерывает цикл, и остальные ничего не получают. Вызов оборачивают в try/catch и перебрасывают ошибку через queueMicrotask, чтобы она не потерялась.
Забывают про утечки. Подписка из компонента без отписки держит компонент в памяти. Node на этот случай предупреждает после десяти слушателей на одно событие, порог меняется через setMaxListeners.