> Как реализовать функцию каррирования с использованием замыканий в JavaScript (JavaScript)
Уровень: senior · Роль: frontend · Категория: Технические вопросы
Компании: циан, Яндекс
Стек: JavaScript
> Пример ответа
Короткий ответ
Каррирование - это трансформация функции от нескольких аргументов в последовательность функций, каждая из которых принимает один аргумент. Реализуется через замыкание: внешняя функция сохраняет переданные аргументы в своей области видимости и возвращает новую функцию, ожидающую следующий аргумент. Процесс повторяется, пока не будут собраны все аргументы, после чего выполняется исходная функция.
Подробное объяснение
Каррирование базируется на двух ключевых механизмах JavaScript: замыкания и функции высшего порядка. Замыкание позволяет внутренней функции "запомнить" аргументы, переданные внешней функции, даже после завершения её выполнения. Функция высшего порядка возвращает другую функцию, создавая цепочку вызовов.
Базовая реализация для фиксированного числа аргументов выглядит так: функция curry(fn) принимает целевую функцию и возвращает вложенные функции, каждая из которых накапливает аргументы через замыкание. Когда количество собранных аргументов достигает fn.length (количество параметров), вызывается исходная функция.
Для произвольного числа аргументов используется рекурсия: функция проверяет, передан ли аргумент. Если да - добавляет его в накопленный массив через замыкание и возвращает новую функцию. Если нет - выполняет накопленные аргументы. Это позволяет вызывать каррированную функцию как с частичным применением, так и с пустым вызовом для завершения.
Важный trade-off: каррирование увеличивает гибкость кода (возможность частичного применения, композиция), но добавляет накладные расходы на создание замыканий и может ухудшить читаемость при чрезмерном использовании.
На практике
Каррирование полезно в сценариях, где нужно многократно вызывать функцию с одними и теми же первыми аргументами. Например, в конфигурации middleware, обработке событий или работе с API, где базовый URL и токен передаются один раз, а остальные параметры - позже.
В реальных проектах редко пишут каррирование вручную - используют библиотеки вроде lodash (_.curry). Однако понимание реализации необходимо для отладки и оптимизации. Каррирование также тесно связано с функциональной композицией (compose, pipe), где оно позволяет создавать pipeline из функций с одним аргументом.
Пример кода
JAVASCRIPT// Базовая реализация для фиксированного числа аргументовfunction curry(fn) {return function curried(...args) {if (args.length >= fn.length) {return fn.apply(this, args);}return function(...nextArgs) {return curried.apply(this, args.concat(nextArgs));};};}// Пример использованияfunction sum(a, b, c) {return a + b + c;}const curriedSum = curry(sum);console.log(curriedSum(1)(2)(3)); // 6console.log(curriedSum(1, 2)(3)); // 6console.log(curriedSum(1)(2, 3)); // 6// Реализация для произвольного числа аргументов (с сигналом завершения)function curryArity(fn) {const args = [];return function curried(arg) {if (arg === undefined) {return fn(...args);}args.push(arg);return curried;};}// Пример с пустым вызовом для завершенияfunction multiply(...nums) {return nums.reduce((acc, n) => acc * n, 1);}const curriedMultiply = curryArity(multiply);console.log(curriedMultiply(2)(3)(4)()); // 24
Как отвечать на собеседовании
Начни с чёткого определения каррирования и его связи с замыканиями. Покажи понимание разницы между каррированием и частичным применением (partial application). Объясни, как работает накопление аргументов через замыкание и проверка fn.length. Упомяни, что в реальном коде каррирование чаще используется через библиотеки, но реализация важна для понимания функционального программирования. Приведи пример, где каррирование упрощает код (например, конфигурация HTTP-клиента). Будь готов обсудить производительность: создание множества замыканий может быть дорогим.
Что проверяет интервьюер
- Понимание замыканий и области видимости
- Умение работать с функциями высшего порядка
- Знание
argumentsили rest-параметров - Понимание
fn.lengthи его ограничений - Способность объяснить разницу между каррированием и частичным применением
- Навыки рекурсивного мышления
- Осознание trade-off между гибкостью и производительностью
Типичные ошибки
- Путают каррирование с частичным применением (каррирование возвращает цепочку функций по одному аргументу, частичное применение фиксирует часть аргументов)
- Забывают про контекст (
this) при использовании методов объекта - Не учитывают, что
fn.lengthне работает для функций с rest-параметрами (...args) - Создают бесконечную рекурсию из-за неправильного условия выхода
- Пытаются каррировать функции с необязательными аргументами без явного сигнала завершения
- Используют глобальные переменные вместо замыкания для хранения аргументов
> Похожие задачи по frontend
Как реализуется инкапсуляция в JavaScript
Как предотвратить двойной заказ при двойном клике на кнопку?
Какие проверки можно применить к массиву в JSON
Как обрабатываются call stack и очереди в JavaScript
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью