Автор оригинала: Bill Sourour.
Я написал свои первые несколько строк JavaScript недолго после того, как язык был изобретен. Если вы сказали мне в то время, что я бы один день пишу серия статей О Элегантный Узоры в JavaScript, я бы смеялся за тобой из комнаты. Я думал о JavaScript как странный маленький язык, который едва даже квалифицируется как «реальное программирование».
Ну, многое изменилось через 20 лет с тех пор. Теперь я вижу в JavaScript, что Дуглас Крукфорд видел, когда он писал JavaScript: Хорошие части : «Выдающийся, динамический язык программирования … с огромной, выразительной мощностью».
Итак, без дальнейшего ADO, вот замечательный маленький узор, который я использовал в моем коде в последнее время. Я надеюсь, что вы пришли наслаждаться этим так же, как у меня есть.
Получите объект, верните объект (RORO).
Большинство моих функций теперь принимают один параметр типа объект
и многие из них возвращаются или решают значение типа объект
также.
Спасибо частично Разрушение Функция, представленная в ES2015, я обнаружил, что это мощный шаблон. Я даже дал это глупуе имя, «Родо», потому что … Брендинг? ¯ \ _ (ツ) _/¯
Вот некоторые причины, почему вам понравится этот шаблон:
- Названные параметры
- Очиститель параметров по умолчанию
- Более богатые значения возврата
- Простая функциональная композиция
Давайте посмотрим на каждого.
Названные параметры
Предположим, у нас есть функция, которая возвращает список пользователей в данной роли и предположим, что нам нужно предоставить возможность, включая контактную информацию каждого пользователя и другой вариант для включения неактивных пользователей, традиционно мы можем написать:
function findUsersByRole ( role, withContactInfo, includeInactive) {...}
Вызов этой функции может потом выглядеть как:
findUsersByRole( 'admin', true, true)
Обратите внимание, насколько неоднозначными эти последние два параметра являются. На что называется «правда, правда»?
Что произойдет, если наше приложение почти никогда не нуждается в контактной информации, но почти всегда нуждается в неактивных пользователях? Мы должны постоянно бороться с этим средним параметром все время, хотя это не совсем актуально (больше на этом позже).
Короче говоря, этот традиционный подход оставляет нас потенциально неоднозначным, шумным кодексом, который труднее понимать и сложнее писать.
Посмотрим, что произойдет, когда мы получаем единый объект вместо этого:
function findUsersByRole ({ role, withContactInfo, includeInactive}) {...}
Обратите внимание на нашу функцию выглядит почти идентична, за исключением того, что Мы поставили скобки вокруг наших параметров Отказ Это указывает на то, что вместо того, чтобы получать три различных параметра, наша функция теперь ожидает одного объекта с свойствами, названными с именем роль
, сcontactinfo.
и вспомогательное
Отказ
Это работает из-за функции JavaScript, представленной в ES2015, называется Разрушение Отказ
Теперь мы можем назвать нашу функцию, как это:
findUsersByRole({ role: 'admin', withContactInfo: true, includeInactive: true})
Это гораздо менее неоднозначно и намного легче читать и понимать. Кроме того, опускание или повторное упорядочение наших параметров больше не является проблемой, поскольку теперь они являются именованными свойствами объекта.
Например, это работает:
findUsersByRole({ withContactInfo: true, role: 'admin', includeInactive: true})
И так ли это:
findUsersByRole({ role: 'admin', includeInactive: true})
Это также позволяет добавлять новые параметры, не нарушая старый код.
Одна важная заметка вот что, если мы хотим, чтобы все параметры были необязательными, другими словами, если следующее является действительным вызовом …
findUsersByRole()
… Нам нужно установить значение по умолчанию для нашего объекта параметра, например:
function findUsersByRole ({ role, withContactInfo, includeInactive} = {}) {...}
Дополнительное преимущество использования деструкции для нашего объекта параметра состоит в том, что он способствует неизменности. Когда мы разрушаем объект
Во время нашей функции мы назначаем свойства объекта новым переменным. Изменение значения этих переменных не изменит исходный объект.
Рассмотрим следующее:
const options = { role: 'Admin', includeInactive: true}
findUsersByRole(options)
function findUsersByRole ({ role, withContactInfo, includeInactive} = {}) { role = role.toLowerCase() console.log(role) // 'admin' ...}
console.log(options.role) // 'Admin'
Хотя мы меняем ценность роль
Значение Опции. Роль
остается неизменной.
Пока так хорошо, да?
Очиститель параметров по умолчанию
С функциями JavaScript ES2015 получили возможность определить параметры по умолчанию. На самом деле, мы недавно использовали параметр по умолчанию, когда мы добавили = {}
к объекту параметра на нашем Findusersbyrole
функция выше.
С традиционными параметрами по умолчанию наше Findusersbyrole
Функция может выглядеть так.
function findUsersByRole ( role, withContactInfo = true, includeInactive) {...}
Если мы хотим установить вспомогательное
к правда
Мы должны явно пройти неопределенный
Как ценность для сcontactinfo
Чтобы сохранить по умолчанию, как это:
findUsersByRole( 'Admin', undefined, true)
Насколько отвратительно это?
Сравните его с использованием объекта параметра, как так:
function findUsersByRole ({ role, withContactInfo = true, includeInactive} = {}) {...}
Теперь мы можем написать …
findUsersByRole({ role: 'Admin', includeInactive: true})
… и наше значение по умолчанию для сcontactinfo
сохраняется
Бонус: Требуемые параметры
Как часто вы написали что-то вроде этого?
function findUsersByRole ({ role, withContactInfo, includeInactive} = {}) { if (role == null) { throw Error(...) } ...}
Что, если я сказал вам, что вы можете использовать параметры по умолчанию для проверки необходимых параметров?
Во-первых, нам нужно определить требуется парам ()
Функция, которая бросает ошибку.
Нравится:
function requiredParam (param) { const requiredParamError = new Error( `Required parameter, "${param}" is missing.` )
// preserve original stack trace if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace( requiredParamError, requiredParam ) }
throw requiredParamError}
Теперь мы можем установить вызов требуется парам
Как значение по умолчанию для роль
, вот так:
function findUsersByRole ({ role = requiredParam('role'), withContactInfo, includeInactive} = {}) {...}
С вышеуказанным кодом, если кто-то звонит Findusersbyrole
без снабжения роль
Они получит Ошибка
. что говорит Необходимый параметр «Роль» отсутствует.
Технически, мы можем использовать эту технику с регулярными параметрами по умолчанию; Мы не обязательно нужны объект. Но этот трюк был слишком полезным, чтобы не упомянуть.
Более богатые значения возврата
Функции JavaScript могут вернуть только одно значение. Если это значение – это объект
Он может содержать намного больше информации.
Рассмотрим функцию, которая сохраняет Пользователь
в базу данных. Когда эта функция возвращает объект, он может обеспечить большую информацию для абонента.
Например, общий шаблон – это данные «UPSERT» или «MERGE» в функции сохранения. Это означает, что мы вставляем строки в таблицу базы данных (если они еще не существуют) или их обновляют (если они существуют).
В таких случаях было бы удобно знать, была ли операция, выполняемая нашей функцией сохранения, была ВСТАВЛЯТЬ
или Обновить
Отказ Было бы также полезно получить точное представление именно то, что было сохранено в базе данных, и было бы хорошо узнать статус операции; Это добилось успешным, оно ожидается как часть большей транзакции, это было время время ожидания?
При возврате объекта легко сообщать все эту информацию одновременно.
Что-то вроде:
async saveUser({ upsert = true, transaction, ...userInfo}) { // save to the DB return { operation, // e.g 'INSERT' status, // e.g. 'Success' saved: userInfo }}
Технически вышеперечисленное возвращает Обещание
это решает к объект
Но ты получил идею.
Простая функциональная композиция
Мы можем составить функции вместе с помощью труба
Функция, которая выглядит что-то подобное:
function pipe(...fns) { return param => fns.reduce( (result, fn) => fn(result), param )}
Вышеуказанная функция принимает список функций и возвращает функцию, которая может применить список слева направо, начиная с заданного параметра, а затем передавать результат каждой функции в списке к следующей функции в списке.
Не волнуйтесь, если вы смущены, есть пример ниже, который должен прояснить вещи.
Одним из ограничений этого подхода является то, что каждая функция в списке должна получить только один параметр. К счастью, когда мы родо, это не проблема!
Вот пример, где у нас есть Сохранить
Функция, что трубы A UserInfo
Объект через 3 отдельных функция, которые подтверждают, нормализуют и сохраняют информацию о пользователе в последовательности.
function saveUser(userInfo) { return pipe( validate, normalize, persist )(userInfo)}
Мы можем использовать Параметр отдыха в нашем проверить
, нормализовать
и сохранить
Функции для разрушения только значения, которые требуют каждую функцию и все еще передают все обратно к абонеру.
Вот немного кода, чтобы дать вам суть этого:
function validate({ id, firstName, lastName, email = requiredParam(), username = requiredParam(), pass = requiredParam(), address, ...rest}) { // do some validation return { id, firstName, lastName, email, username, pass, address, ...rest }}
function normalize({ email, username, ...rest}) { // do some normalizing return { email, username, ...rest }}
async function persist({ upsert = true, ...info}) { // save userInfo to the DB return { operation, status, saved: info }}
До Ro или нет, это вопрос.
Я сказал у самого начала, Большинство Из моих функций получают объект и Многие из них тоже возвращают объект.
Как и любой шаблон, Roro следует рассматривать как просто другой инструмент на нашем поле для инструментов. Мы используем его в местах, где он добавляет значение, сделав список параметров более четкими и гибкими, и сделав возвращаемое значение более выразительным.
Если вы пишете функцию, которая будет только для получения одного параметра, а затем получение объект
это переплет. Точно так же, если вы пишете функцию, которая может сообщать четкий и интуитивно понятный ответ на звонящий, вернув простое значение, нет необходимости возвращать объект
Отказ
Пример, где я почти никогда не RORO – это функции утверждения. Предположим, у нас есть функция положительное целое число
Это проверяет, является ли данный параметр положительным целым числом, такая функция, скорее всего, не выиграет от Roro вообще.
Если вам понравилось эту статью, пожалуйста, разбейте значок аплодисментов кучу раз, чтобы помочь распространить слово. И если вы хотите прочитать больше, пожалуйста, подпишитесь на мой рассылку Mastery Mastery ниже.
Оригинал: “https://www.freecodecamp.org/news/elegant-patterns-in-modern-javascript-roro-be01e7669cbd/”