Как транспилировать ES модули с помощью Webpack
2 месяца назад·10 мин. на чтение
В экосистеме JavaScript webpack выделяется как основной инструмент для объединения нашего кода. Но прежде чем мы углубимся в то, как webpack это делает, разберемся, что такое ES-модули.
Что такое ES-модули?
ES модули (ESM) являются рекомендуемым способом написания кода как для Node.js, так и для браузера. В настоящее время модули ES поддерживаются всеми основными браузерами и представляют собой официальный стандартный формат для упаковки и повторного использования кода JavaScript в Интернете. Мы можем определять и использовать пакеты в модульной системе с ключевыми словамиexport
и import
.
Импорт и экспорт функций
Ниже приведен пример импорта и экспорта функции с синтаксисом ES модулей:Обратите внимание, ключевое слово// smallestNumber.js export function smallestNumber(arr) { let smallestNumber = arr[0] let smallestIndex = 0 for(let i = 1; i < arr.length; i++) { if(arr[i] < smallestNumber) { smallestNumber = arr[i] smallestIndex = i } } return smallestNumber; }
export
полезно, поскольку оно позволяет нам сделать нашу функцию smallestNumber
доступной для других модулей, которым необходимо ее вызвать. Помимо обычных функций, мы можем экспортировать константы, классы и даже просто переменные. Также обратите внимание, что у нас может быть экспорт по умолчанию с ESM.
// result.js import { smallestNumber } from './smallestNumber.js'; console.log(smallestNumber([3,2,5,6,0,-1])) // возвращает -1
Транспилирование кода
В конце концов, мы сосредоточимся на механике процесса транспиляции кода в следующем разделе, но перед этим давайте в первую очередь разберемся в важности этого процесса. Как мы уже упоминали ранее, старые версии JavaScript, такие как ES5, должны иметь возможность запускать наш новый код, а также понимать его синтаксис. Это означает, что должен быть способ полной обратной совместимости языка. Обратная совместимость является одним из наиболее важных аспектов, которые следует учитывать и расставлять приоритеты при добавлении новых функций в язык программирования. Для ESM среда выполнения Node.js может определить систему/формат модуля, который она должна использовать по умолчанию, на основе поляtype
, если оно установлено в module
в package.json
, который находится в корне проекта.
Приведенная выше настройка позволяет создавать все файлы, которые находятся на том же уровне структуры папок, что и// package.json { "type": "module" }
package.json
по умолчанию ESM. В качестве альтернативы мы можем установить тип commonjs
, и среда выполнения Node заставит все файлы соответствовать системе Common JS Module по умолчанию. Хотя обычно это поведение по умолчанию, если поле type
не указано.
По сути, когда модули помечены как модули ECMAScript, среда выполнения Node использует другой шаблон или метод для разрешения импорта файлов. Например, импорт теперь более строгий, что означает, что мы должны добавить полное имя файла с их расширениями для относительных путей или запросов, в зависимости от обстоятельств.
Что такое транспиляция кода?
С введением новой версии JavaScript и изменениями в синтаксисе (также известным как ES2015), введением TypeScript, надмножества JavaScript и других достижений, таких как, например, CoffeeScript, написание JavaScript, который работает везде, уже не так просто, как раньше. Как мы, возможно, уже знаем, разные браузеры имеют разные движки JavaScript, и их различные уровни принятия и поддержки этих новых функций JS могут различаться, потому что они не соответствовали спецификации языка в одинаковое время. Это привело к тому, что код может работать в одном браузере и не работать в другом. Таким образом, суть транспиляции заключается в том, чтобы иметь возможность конвертировать новый синтаксис JS ES2015 в старый синтаксис ES5, чтобы код мог работать в старых браузерах. Например, шаблонные литералы или, оператор слияения null (??
) и другие функции ES2015 или ES2015+ по-прежнему не имеют полной поддержки браузеров и серверной среды выполнения, поэтому нам может потребоваться транспилировать наш код для поддержки этих версий.
В основном, инструменты, называемые загрузчиками (loaders), такие как Babel, Traceur и т. д., используются в сочетании с webpack для транспиляции кода.
На высоком уровне транспиляторы работают на разных языках программирования, считывая исходный код строка за строкой и выдавая эквивалентный результат. Например, мы можем захотеть транспилировать кодовую базу TypeScript в старый добрый JavaScript.
В целом, транспиляторы позволяют нам уверенно использовать новые, нестандартизированные возможности JavaScript. В настоящее время лучшим подходом к работе с модулями ES как в среде Node.js, так и в среде браузера является их транспилирование в формат модуля CommonJS с помощью Babel.
Что такое Webpack ?
Webpack — это инструмент сборки, который помогает объединить наш код и его зависимости в один файл JavaScript. Можно также сказать, что webpack — это своего рода сборщик статических модулей для JavaScript-приложений. Это связано с тем, что он применяет такие методы, как встряхивание дерева и компиляция (которая состоит из этапов транспиляции и минификации) к нашему исходному коду. Упаковщики, такие как webpack, работают рука об руку с транспиляторами. Это означает, что это совершенно разные, но дополняющие друг друга наборы инструментов. Поэтому нам нужно настроить webpack для работы с транспилятором — скажем, Babel. Как мы упоминали ранее, транспиляторы либо выполняют работу по компиляции одного языка в другой, либо делают язык обратно совместимым. Webpack довольно хорошо работает с Babel, а также легко настраивается. Например, мы можем настроить Babel для работы с webpack, создав конфигурационный файл webpack (webpack.config.js
) использование плагина Babel — по сути, экосистема плагинов webpack — это то, что делает webpack тем, что есть.
С другой стороны, Babel может быть сконфигурирован с помощью babel.config.js
файл или .babelrc
файл.
Почему webpack?
Как вы, возможно, знаете, webpack поддерживает несколько типов модулей из коробки, включая модули CommonJS и ES. Webpack также работает как на клиентском, так и на серверном JavaScript, поэтому с помощью webpack мы также можем легко работать с ресурсами, такими как изображения, шрифты, таблицы стилей и так далее. Он остается действительно мощным инструментом, поскольку автоматически строит и выводит граф зависимостей на основе импорта и экспорта файлов (поскольку, по сути, каждый файл является модулем). Сочетание этого с загрузчиками и плагинами делает webpack отличным инструментом в нашем арсенале. Более подробно о том, как это работает под капотом, можно прочитать в документации. Что касается плагинов, webpack также имеет богатую экосистему плагинов. Плагины поддерживают webpack в выполнении некоторой грязной работы, такой как оптимизация пакетов, управление ресурсами и так далее. Подводя итог, можно сказать, что комплектация с такими инструментами, как webpack, является самым быстрым способом работы или обеспечения обратной совместимости с модулями в наши дни, поскольку ESM постепенно набирает обороты в качестве официального стандарта для повторного использования кода в экосистеме. Webpack также поддерживает загрузчики, которые помогают ему решать, как обрабатывать, объединять и обрабатывать несобственные модули или файлы. Важно отметить, как webpack относится к загрузчикам. Загрузчики оцениваются и выполняются снизу вверх. Таким образом, последний загрузчик выполняется первым, и так далее, и тому подобное, именно в таком порядке. В этой статье мы сосредоточимся на том, как webpack транспилирует или обрабатывает модули ECMAScript.Использование загрузчиков для транспиляции
Загрузчики преобразуют файлы из одного языка программирования в другой. Например,ts-loader
может преобразовывать или транспилировать TypeScript в JavaScript. Обычно мы используем загрузчики в качестве зависимостей разработки. Например, давайте посмотрим, как мы можем использовать ts-loader
.
Для установки мы можем выполнить следующее:
Затем мы можем использовать этот загрузчик, чтобы указать webpack правильно обрабатывать все файлы TypeScript в нашем исходном коде. Ознакомьтесь с примеромnpm install --save-dev ts-loader
webpack.config.js
файл ниже.
Здесь, как мы уже упоминали, мы говорим webpack обрабатывать все пути к файлам, оканчивающиеся наmodule.exports = { module: { rules: [ { test: /.ts$/, use: 'ts-loader' }, ], }, };
.ts
и транспилировать их в синтаксис JavaScript, понятный браузеру и среде выполнения Node. Это означает, что загрузчики также могут работать в среде Node.js и, следовательно, также следовать стандарту разрешения модулей.
Соглашения об именовании загрузчиков
Общий способ именования загрузчиков является последовательным, так как загрузчики именуются своим именем и дефисом — обычно какxxx-loader
. Например, babel-loader
, ts-loader
и так далее.
Для нашего примера использования нас особенно интересует ESNext
или babel-loader
, загрузчик, созданный и поддерживаемый сообществом. Более подробную информацию о загрузчиках можно найти в документации webpack.
Некоторые основные понятия webpack
Чтобы понять, как работает webpack, в этом разделе рассматриваются некоторые высокоуровневые концепции, о которых должны знать читатели. Как мы упоминали ранее, webpack использует граф зависимостей, что означает, что он рекурсивно строит связь, включающую каждый модуль, который нужен приложению или от которого зависит, а затем объединяет все эти модули в выходной файл, готовый к использованию. Это означает, что webpack должен иметь точку входа — и это действительно так. При настройке конфигурации webpack точкой входа является начало проверок webpack пути к файлу, прежде чем он начнет строить внутренний граф зависимостей для нашего приложения. Обратите внимание, что webpack также поддерживает несколько точек входа для нашего приложения.Чтобы добавить несколько точек входа, мы можем использовать массив. В webpack v5 теперь у нас может быть пустой объект входа. После того, как webpack построит граф зависимостей внутри и завершит процесс объединения, он должен вывести пакет в другой путь к файлу, который нам затем нужно обработать. Это то, что делаетmodule.exports = { entry: ['./path/to/my/entry1/file1.js', './path/to/my/entry2/file2.js'] };
output
. Он сообщает webpack, какой путь выдавать созданные пакеты и как называются файлы.
Начиная с версии 4.0.0, webpack не требует конфигурационного файла для объединения проекта, хотя и предполагает, что вы определили точку входа в приложение с папкой и путем к файлу какconst path = require('path'); module.exports = { entry: './path/to/my/entry/file.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js', }, };
src/index.js
, и что вывод упакован в dist/main.js
путь к папке, минимизированный, оптимизированный и готовый к продакшену.
Настройка webpack и Babel
В webpack была реализована полная поддержка использования встроенного синтаксиса модуля ES2015. Это означает, что мы можем использовать операторыimport
и export
, не полагаясь на внешние инструменты транспиляции или зависимости, такие как Babel. Тем не менее, все же рекомендуется настроить Babel, на случай, если есть другие, более новые функции ES2015+, которые webpack еще не принял во внимание.
В зависимости от форматов модулей webpack проверяет ближайший package.json
и обеспечивает соблюдение соответствующих рекомендаций. При использовании webpack для объединения нашего кода обычно рекомендуется придерживаться синтаксиса одного модуля, чтобы позволить webpack правильно обрабатывать собранный вывод и, следовательно, предотвращать нежелательные ошибки.
Чтобы начать, нам нужно убедиться, что на наших компьютерах установлен интерфейс командной строки webpack. Затем мы можем использовать команду CLI init
, чтобы быстро запустить конфигурацию webpack в соответствии с требованиями нашего проекта. Мы можем сделать это, просто выполнив npx webpack-cli init
и соответствующим образом отвечая на запросы.
Теперь нам нужно скомпилировать наш код ES2015 в ES5, чтобы мы могли использовать его в различных средах браузера или средах выполнения. Для этого нам нужно установить Babel и все его зависимости, необходимые для webpack.
Давайте установим следующее:
- Babel Core
- Babel Loader, webpack загрузчик, который взаимодействует с Babel Core
По окончанию установки нашnpm i webpack webpack-cli webpack-dev-server @babel/core @babel/preset-env babel-loader rimraf -D
package.json
должно выглядеть следующим образом:
Обратите внимание, что наши зависимости закреплены, чтобы обеспечить согласованность при запуске нашего приложения в будущем. Пакет{ "name": "webpack-demo", "version": "1.0.0", "description": "A demo of webpack with babel", "main": "dist/bundle.js", "scripts": { "build": "node_modules/.bin/webpack --config webpack.config.js --mode=production", "watch": "node_modules/.bin/webpack --config webpack.config.js --mode=development -w", "prebuild:dev": "rimraf dist" }, "author": "Name", "license": "MIT", "dependencies": {}, "devDependencies": { "@babel/core": "^7.16.0", "@babel/preset-env": "^7.16.4", "babel-loader": "^8.2.3", "rimraf": "^3.0.2", "webpack": "^5.64.3", "webpack-cli": "^4.9.1", "webpack-dev-server": "^4.5.0" }, "type": "module" }
@babel/preset-env
преобразует весь код ES2015–ES2020 в ES5 или практически в любую целевую среду, которую мы укажем в целевых параметрах. Обычно он нам нужен, когда мы намереваемся установить конкретную цель, которая затем позволяет Babel нацелиться на эту конкретную среду.
Этот пакет в основном проверяет указанное целевое окружение по своему внутреннему отображению, а затем составляет список плагинов, которые он передает в Babel для транспиляции кода. Это приводит к уменьшению количества пакетов JavaScript.
Поэтому, если мы не укажем target
, размер выводимого кода будет больше, потому что по умолчанию плагины Babel группируют синтаксические функции ECMAScript в коллекцию связанных функций.
Более того, с опцией меньшего размера пакета и большего прироста производительности, мы можем использовать babel/preset-modules
, которые в конечном итоге будут объединены в ядро @babel/preset-env
.
Настройка webpack.config.js
Теперь давайте перейдем к настройке нашего файла webpack. Создайте новый webpack.config.js
в корне нашего проекта.
В приведенном выше конфиге мы установили для поляimport path from "path"; import { fileURLToPath } from "url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); export default { entry: './src/index.js', output: { path: path.resolve(__dirname, './dist'), filename: 'bundle.js', }, experiments: { outputModule: true, }, plugins: [ //empty pluggins array ], module: { // https://webpack.js.org/loaders/babel-loader/#root rules: [ { test: /.m?js$/, loader: 'babel-loader', exclude: /node_modules/, } ], }, devtool: 'source-map' }
outputModule: true
, что необходимо, если мы намерены использовать webpack для компиляции публичной библиотеки, предназначенной для использования другими. babel-loader
загружает код ES2015+ и транспилирует его в ES5 с помощью Babel.
Как вы также можете видеть в конфигурационном файле, у нас есть свойство module
, которое имеет свойство rules
, содержащее массив для настройки отдельных загрузчиков, которые могут нам понадобиться для нашей конфигурации webpack.
Здесь мы добавили загрузчик webpack и установили необходимые параметры в соответствии с требованиями нашего проекта.
Обратите внимание, что test
представляет собой регулярное выражение, соответствующее абсолютному пути к каждому файлу и проверяющее наличие расширений файлов. В приведенном выше примере мы проверяем, заканчивается ли наш файл на .mjs
или .js
расширение.
Использование (и неиспользование) .babelrc
файл
Теперь мы можем сконфигурировать Babel, создав .babelrc
, также в корне нашего проекта. Содержимое файла показано ниже:
В случае, если мы не хотим использовать{ "presets": [ [ "@babel/preset-env", { "targets": { "esmodules": true } } ] ] }
.babelrc
, мы также можем добавить пресеты в объект options
внутри массива rules
, например:
module: { rules: [ { test: /.m?js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env', "es2015", "es2016"], } } } ] }
Настройка пресетов и добавление плагинов
Нам нужно установить пресеты так, чтобы функции ES2015 в нашем коде могли быть преобразованы в ES5. В массивеoptions
мы также можем добавить плагины, которые мы хотим добавить в нашу конфигурацию.
Например, мы можем добавить эту plugins: ['@babel/plugin-transform-runtime']
, которая устанавливает среду выполнения Babel, которая отключает автоматическое внедрение среды выполнения для каждого файла, чтобы предотвратить раздувание.
Чтобы включить это в наш код, нам нужно установить их, выполнив:
Мы также должны добавитьnpm install -D @babel/plugin-transform-runtime
@babel/runtime
в качестве зависимости, выполнив npm install @babel/runtime
.
Также обратите внимание, что в объекте rules
мы указываем webpack исключить файлы в папке node_modules
со свойством exclude
. Это сделано для того, чтобы у нас был более быстрый процесс объединения, так как мы не хотим объединять нашу node_modules
папку.
Также бывают случаи, когда мы хотим, чтобы webpack обрабатывал модули ES 2015. Чтобы разрешить это, нам нужно использовать плагин ModuleConcatenationPlugin
. Этот плагин включает некоторую форму поведения конкатенации в webpack, называемую поднятием области, что является возможной благодаря синтаксису ESM. По умолчанию этот плагин уже включен в рабочем режиме и отключен в противном случае.
Итоги
Чтобы избежать проблем с совместимостью, нам нужно транспилировать наш код из ES2015 в ES5. Здесь на помощь приходит webpack. Webpack объединяет наш код и выводит транспилированную версию в целевой файл, как указано в конфигурационном файле. В нашем конфигурационном файле webpack правила модуля позволяют нам указывать различные загрузчики, что является простым способом отображения загрузчиков. В этом посте мы использовали только загрузчик Babel, но есть много других загрузчиков, которые мы также можем использовать в экосистеме. Что касается улучшений и изменений в последнем выпуске webpack 5, теперь есть встроенная поддержка асинхронных модулей. Как следует из названия, асинхронные модули основаны наPromise
и поэтому не разрешаются синхронно. Импорт асинхронных модулей через require()
теперь также будет возвращать Promise
, который разрешается в их экспорт.Что такое каррирование? Функциональное программирование
2 года назад·5 мин. на чтение
В этой статье на простых и доступных примерах рассмотрим одну из концепций функционального программирования - Каррирование.
Это серия статей о функциональном программировании:
Представим, что у нас есть функция
Итак, сначала мы создали
Пояснение к функции
Функция
Каждый раз, когда мы вызываем
Каррирование действительно внесло улучшения, нам не нужно каждый раз указывать локаль. Вместо этого каррированная функция
Для лучшего понимания можно развернуть каррирование и вместо этого использовать обычные функции.
- Парадигмы программирования
- Композиция
- Функторы
- Каррирование (рассматривается в этой статье)
- Чистые функции
- Функции первого класса
Что такое каррирование?
Каррированная функция — это функция, которая продолжает возвращать функции до тех пор, пока не будут отправлены все ее параметры.Как работает каррирование?
Предположим, есть функция сложенияadd
.
Простейшая реализация каррирования — заставить функцию возвращать функцию и т. д.const add = (a, b) => a + b
Эту функцию можно использовать так.const add = (a) => (b) => a + b
const addOne = add(1) // addOne = (b) => 1 + b
curry
, которая принимает функцию и каррирует ее.
Как мы видим,const add = curry((a, b) => a + b)
curry
— это функция, которая использует другую функцию для ленивой обработки параметров. Итак, теперь мы можем вызвать ее следующим образом.
const addOne = add(1) // addOne = (b) => 1 + b
addOne
, передав 1
в качестве первого параметра (a
) каррированной функции добавления. Что привело к другой функции, которая ожидает остальные параметры, где логика добавления не будет выполняться, пока не будут предоставлены все параметры.
Теперь, передаваяaddOne(2) // 3
2
(как b
) в addOne
выполняет логику 1 + 2
.
Пояснение к функции curry
Функция curry
принимает функцию и делает ее параметры ленивыми, другими словами, вы предоставляете эти параметры по мере необходимости. Так же, как addOne
.
Вы по-прежнему можете вызывать каррированную версию функции добавления следующим образом.
Таким образом, он либо принимает аргументы по частям, либо все аргументы сразу.const three = add(1, 2)
Для чего нужно каррировать функции?
Каррирование делает код:- Чище
- Уменьшает количество повторяющихся параметров и делает код более лаконичным
- Более компонуемым
- Переиспользуемым
Почему каррирование делает код лучше?
Некоторые функции принимают конфигурацию в качестве входных данных
Если у нас есть функции, которые принимают конфигурацию (начальные настройки), нам лучше их каррировать, потому что эти конфигурации, вероятно, будут повторяться снова и снова. Предположим, что у нас есть функция перевода, которая принимает локаль и текст для перевода.Использование будет выглядеть так.const translate = (locale, text) => { /* логика перевода */ }
translate('ru', 'Hello') translate('ru', 'Goodbye') translate('ru', 'How are you?')
translate
, мы должны указывать язык и текст. Что является избыточным для предоставления локали при каждом вызове.
Но вместо этого давайте каррируем translate
следующим образом.
Теперьconst translate = curry( (locale, text) => { /* логика перевода */ } ) const translateToRu = translate('ru')
translateToRu
имеет ru
в качестве locale
, предоставленного каррированной функции translate
, и ожидает текста. Мы можем использовать это так.
translateToRu('Hello') translateToRu('Goodbye') translateToRu('How are you?')
translateToRu
содержит locale
из-за каррирования.
После каррирования - в этом конкретном примере код стал:
- чище
- менее многословным и менее избыточным.
На практике
На практике у нас есть динамическая локаль (у каждого пользователя свой язык), может бытьfr
, en
, de
или что-то еще. Поэтому вместо этого лучше переименовать translateToRu
в translateTo
, где translateTo
может быть загружен с любой локалью.
Теперь у нас есть translate
, который принимает locale
как конфигурацию и text
как данные. Благодаря тому, что translate
каррирован, мы смогли отделить параметры конфигурации от данных.
Зачем отделять конфигурации от данных?
Многие компоненты и функции нуждаются в использовании некоторой функциональности (в нашем случае,translateTo
), но не должны или не могут знать о части конфигурации (locale
). Эти компоненты или функции имеют только часть данных (text
). Таким образом, эти функции смогут использовать эту функцию без необходимости знать о части конфигурации.
Таким образом, этот компонент или функция будут меньше связаны с системой, что сделает компоненты более компонуемыми и более удобными в сопровождении.
Когда применять каррирование?
Когда мы знаем, что в функции есть конфигурация и есть данные, лучше их каррировать. Каррирование даст нам возможность их разделить. И это признак зрелого дизайна системы. Потому что одним из основных столпов качества кода является разделение задач. Даже если функции нужны все параметры для правильной работы, мы все равно лучше знаем, когда передавать параметры и на каком уровне приложения.Связь между замыканием и каррированием
Замыкание - это функция, возвращаемая «родительской» функцией и имеющая доступ к внутреннему состоянию родительской функции. Каррирование всегда приводит к замыканию. Потому что каждая функция, возвращаемая каррированной функцией, будет снабжена внутренним состоянием родителей.Примеры каррирования
Перед тем как продолжить
Добавим некоторые утилиты, чтобы мы могли перейти к примерам. Прототип массива имеет такие утилиты, какfilter
, map
и другие. Но они не поддерживают каррирование, потому что используют запись через точку (.
).
Итак, давайте конвертируем их в каррируемый формат.
Теперь мы можем использовать их так.const filter = (fn, list) => list.filter(fn) const map = (fn, list) => list.map(fn) const startsWith = (starter, s) => s.startsWith(starter)
Мы исключили запись через точку и передали обработанные данные в качестве последнего параметра. Затем мы их каррируем. Функцияconst lessThan21 = user => user.age < 21 // Вместо такого использования... const filteredUsers = users.filter(lessThan21 ) // ...будем использовать такое const filteredUsers = filter(lessThan21, users)
curry
будет принимать функцию и возвращать каррированную функцию.
const filter = curry((fn, list) => list.filter(fn)) const map = curry((fn, list) => list.map(fn)) const startsWith = curry((starter, s) => s.startsWith(starter))
Пример 1
Дан список чисел, нужно увеличить все числа на 1. Вход:[1, 2, 3, 4, 5]
Выход: [2, 3, 4, 5, 6]
Реализация:
// каррированная функция add была определена ранее const addOne = add(1) const incrementNumbers = map(addOne) const incrementedNumbers = incrementNumbers(numbers)
Пример 2
Дана строка, оставить все слова, начинающиеся с буквыc
.
Вход: "currying javascript function”
Выход: “currying”
Реализация:
const startsWithC = startsWith('c') const filterStartsWithC = filter(startsWithC) const filteredWords = filterStartsWithC(words)
Пример 3
Дан список диапазонов и список чисел. Создайте массив функций, которые могут фильтровать числа на основе предоставленных диапазонов.Выход: массив функций. Каждая функция может принимать числа и возвращать отфильтрованные числа, которые находятся в заданном диапазоне.const ranges = [ { min: 10, max: 100 }, { min: 100, max: 500 }, { min: 500, max: 999 } ] const numbers = [30, 50, 110, 200, 650, 700, 1000] // 30 и 50 в первом диапазоне // 110 и 200 во втором диапазоне // 650 и 700 в третьем диапазоне // 1000 не принадлежит ни одному диапазону
В этом примере есть двойное каррирование,const isInRange = curry( (range, val) => val > range.min && val < range.max ) const filters = ranges.map((range) => filter(isInRange(range)))
filter
и isInRange
.
filters
теперь представляют собой список функций, каждая из которых ожидает numbers
для обработки.
const isInRange = (range, val) => val > range.min && val < range.max const filters = ranges.map( (range) => (numbers) => numbers.filter( number => isInRange(range, number) ) )