Как реализуется каррирование в JavaScript 🤹🏻♂️
Когда я спрашивал в Сетке (https://set.ki/question/J2WE82r), кто использовал каррирование, и общался за пределами, а также готовился к собеседованиям, выяснилось, что мало кто использует этот приём в реальных проектах. Но на собеседованиях могут попросить решить такую задачу. Меня, например, просили в Касперском, и я тогда честно говоря растерялся.
Потом пришлось разобраться, и я даже использовал этот приём в некоторых своих проектах для валидации и создания UI-компонентов на чистом JS/TS без фреймворков. Это работает, потому что каррирование позволяет создавать специализированные функции из общих.
Каррирование - это преобразование функции с множеством аргументов в последовательность функций, каждая из которых принимает один аргумент. Вместо f(a, b, c) получаем f(a)(b)(c). При этом можно передавать аргументы в любом количестве и порядке, пока не наберётся нужное количество для выполнения исходной функции.
❕Важно различать процесс каррирования и каррированный стиль написания функций❕
Каррирование - это преобразование обычной функции [(a, b, c) → a + b + c] в каррированную [(a) → (b) → (c) → a + b + c] с помощью функции curry.
Каррированный стиль - это когда функция изначально написана в виде [(a) → (b) → (c) → a + b + c] без преобразования.
В первом случае мы берём существующую функцию и каррируем её, во втором - просто используем композицию функций, написанных в каррированном стиле.
💬 Как работает цепочка замыканий? При объявлении функции создаётся внутреннее свойство [[Environment]], которое запоминает текущее лексическое окружение места создания. При вызове функции с первым аргументом создаётся Execution Context и Function Environment Record, где сохраняется привязка параметра. Функция возвращает новую функцию, которая уже имеет замыкание на окружение первой функции через [[Environment]].
💬 При вызове второй функции создаётся новый Execution Context и Function Environment Record, но через цепочку замыканий, которые уже были созданы при объявлении функций, доступны переменные из предыдущих уровней. Каждый уровень каррирования создаёт новую функцию с собственным замыканием при объявлении, которое ссылается на предыдущие уровни через [[OuterEnv]]. При обращении к переменным движок проходит по цепочке Environment Records через [[OuterEnv]] и на каждом уровне вызывает GetBindingValue для получения значения.
💬 Цепочка работает так: первая функция сохраняет первый аргумент в своём Function Environment Record при вызове, вторая функция имеет доступ к первому аргументу через [[OuterEnv]], третья функция имеет доступ к обоим предыдущим аргументам. Всё это происходит через механизм лексических окружений, где каждая функция запоминает окружение места создания через [[Environment]], а не места вызова.
❗️Практические кейсы: валидация, где можно создать специализированные функции проверки, и создание UI-компонентов, где каррирование позволяет настраивать поведение через параметры. Это работает особенно хорошо на чистом JS/TS без фреймворков, где нужно больше контроля над созданием функций.
Как это работает на практике? Для валидации можно создать функцию validate, которая принимает правило и возвращает функцию проверки. Возвращённая функция уже имеет замыкание при объявлении, которое запоминает правило из внешнего окружения. При вызове validate с правилом это правило сохраняется в Function Environment Record, и возвращённая функция может проверить любое значение по сохранённому правилу через замыкание. Так можно создать isEmail, isMinLength - специализированные функции из общей validate. #js #основы
· 22.01
Интересный разбор Александр, хотелось бы больше примеров из реальных кейсов, а так стало понятнее 😁
ответить
коммент удалён
· 22.01
Да, я тут для этого, хочу разбирать интересные базовые темы определенным образом)
ответить
ответ удалён
· 22.01
Какая следующая тема будет, интересно?)
ответить
ответ удалён
· 22.01
Не могу сказать, находит вдохновение - сажусь делать) Пока без мыслей)
ответить
ответ удалён
· 23.01
Пиши еще. )
ответить
ответ удалён