> Как реализовать функцию add в JavaScript, которая накапливает аргументы и возвращает сумму при вызове без аргументов (JavaScript)

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

Компании: Домклик, Kaspersky

Стек: JavaScript

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

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

Функция add с накоплением аргументов и возвратом суммы при вызове без аргументов реализуется через замыкание и переопределение valueOf или Symbol.toPrimitive. При каждом вызове с аргументом она возвращает новую функцию, которая хранит текущую сумму в замыкании. При вызове без аргументов возвращается накопленное значение. Альтернативно можно использовать toString для неявного преобразования.

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

Задача требует создания функции, которая ведет себя как add(1)(2)(3)() - возвращает 6. Ключевая идея - использовать замыкание для хранения аккумулятора. Каждый вызов с аргументом создает новую функцию, которая обновляет сумму и возвращает себя же (или новую функцию с обновленным значением). При вызове без аргументов возвращается финальная сумма.

Для поддержки цепочек вызовов без финального пустого вызова (например, add(1)(2)(3) возвращает 6) необходимо переопределить метод примитивного преобразования - valueOf или Symbol.toPrimitive. Это позволяет JavaScript автоматически преобразовывать функцию в число при контексте, ожидающем число (арифметические операции, сравнения, console.log).

Важно: функция должна быть чистой (без мутации глобального состояния) и поддерживать произвольное количество аргументов в одном вызове, например add(1, 2)(3)().

На практике

В реальных проектах такая конструкция редко встречается напрямую, но понимание замыканий, каррирования и примитивных преобразований критично для работы с функциональными библиотеками (Ramda, Lodash/fp), middleware (Redux), композиции функций и оптимизации вычислений.

Типичные сценарии использования:

  • Ленивые вычисления с накоплением параметров
  • Конфигураторы с fluent API (builder pattern)
  • Функции-валидаторы с цепочками условий

Пример кода

JAVASCRIPT
function add(...args) {
const sum = args.reduce((acc, val) => acc + val, 0);
function inner(...nextArgs) {
if (nextArgs.length === 0) {
return sum;
}
return add(sum, ...nextArgs);
}
inner.valueOf = () => sum;
inner[Symbol.toPrimitive] = () => sum;
return inner;
}
// Использование
console.log(add(1)(2)(3)()); // 6
console.log(add(1, 2)(3, 4)()); // 10
console.log(add(5)(10) + 2); // 17 (через valueOf)
console.log(add(1)(2)(3) == 6); // true (через Symbol.toPrimitive)

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

  1. Начни с объяснения замыкания как основного механизма
  2. Покажи базовую реализацию с пустым вызовом в конце
  3. Объясни, как добавить поддержку неявного преобразования через valueOf/Symbol.toPrimitive
  4. Упомяни edge cases: вызов без аргументов сразу, передача нескольких аргументов, отрицательные числа
  5. Обсуди trade-off: рекурсивный подход vs итеративный, чистота функции, производительность при длинных цепочках

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

  • Понимание замыканий и лексического окружения
  • Знание механизмов приведения типов и примитивных преобразований
  • Умение работать с rest parameters и spread operator
  • Способность проектировать fluent API
  • Понимание функционального программирования (каррирование, частичное применение)
  • Внимание к деталям (обработка пустого вызова, множественных аргументов)

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

  • Использование глобальной переменной для хранения суммы (нарушение чистоты функции)
  • Забывание обработать случай вызова без аргументов сразу (add())
  • Неправильное переопределение valueOf - возврат функции вместо числа
  • Потеря контекста при использовании стрелочных функций в методах объекта
  • Игнорирование множественных аргументов в одном вызове (add(1, 2, 3))
  • Создание бесконечной рекурсии при неправильной реализации цепочки

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

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