Долгое время я восхищался языками программирования, которые пытаются имитировать естественные языки, от которых они произошли. Я часто ищу более естественные способы выражения концепций в программировании.
Как человек, который потратил много времени на работу с довольно унылой встроенной функциональностью JavaScript, я нашел в Ruby (или, точнее, Rails) довольно приятным моментом Числовые расширения ActiveSupport. 3.hours.ago
синтаксис ActiveSupport более похож на английский, чем, например, Moment.js ’moment().subtract(3, 'hours')
.
К сожалению, этот подход включает в себя обезьяну, исправляющую стандартную библиотеку, что я никогда не был поклонником, поскольку он открывает вас для неожиданного поведения. Некоторые люди также делают это в JavaScript, но никогда не чувствовали себя комфортно с идеей прикрепления таких вещей к встроенным типам.
После написания нескольких запросов на вытягивание, регулирующих задержку в setTimeout
, я нашел лучший способ. Конечно, я мог бы просто определить const
в другом месте с красивым описательным именем, например REFRESH_INTERVAL_MILLISECONDS
, но это слишком многословно и создает разрыв между использованием и определением, что делает его менее желательным.
Мне всегда было интересно узнать о новых и разных способах использования имеющихся в моем распоряжении языков программирования, поэтому я начал возиться!
Подход №1: возиться с тегированными шаблонными литералами
Первое, что пришло в голову, - это функция ES6, с которой я в то время возился: тегированные литералы шаблонов - по сути, современный JavaScript-ответ на printf
.
Когда литерал шаблона (технический термин для этих новых строковых литералов в стиле ES6 `surrounded by backticks`
) помечен функцией, функция вызывается с массивом строковых значений и каждой интерполяцией в качестве дополнительного аргумента, оба производных от литерала шаблона. Отсюда вы можете манипулировать или реагировать на каждый кусок строки и возвращать все, что захотите! Если вы хотите узнать больше о шаблонных литералах, Макс Штойбер написал фантастическое введение.
Ваше число и единица времени включены в литерал шаблона. Затем функция тега вернет объект, свойства которого содержат ваш номер в каждой единице времени, поддерживаемой Moment.js.
По общему признанию, это не очень разумный способ использования шаблонных буквенных тегов, но он работал.
Подход # 2: вызов функций в контексте с Babel
Неудовлетворенный как чрезмерно подробным синтаксисом, так и странным использованием шаблонных буквенных тегов, я стал искать больше вещей, которые вы могли бы сделать с ES6 и современными компиляторами.
Оказалось, что Babel, который мы уже использовали для сборки Buildkite, включает в себя некоторые экспериментальные языковые возможности. Именно здесь я нашел экспериментальный сокращенный синтаксис связывания функции.
Хотя это и не предназначено для использования, он позволяет вам передавать значение в функцию от до имени функции . В функции он доступен как this
variable! Используя сокращение bind, я мог бы затем добавить обычный объект Number
, передать его в мои функции и полностью избежать исправления обезьяны! 🙌🏼
Выражение в формате 2::seconds.in(milliseconds)
во время компиляции превращается в seconds.in.call(2, milliseconds)
, что делает его приятным и читаемым, но предоставляет браузеру стандартный код JavaScript.
Это был фантастический хак, и я был немного удивлен, как мне это нравится!
Несколько дней спустя, когда я показывал свой маленький отрывок Глену Мэддерну, он указал, что он может пойти еще дальше…
Подход № 3: рождение Метрика
Сокращение связывания функции может либо call
ваша функция, если указаны аргументы, либо просто bind
. И то, и другое предусмотрено прототипом Function
. В данном случае я использую call
в функции in
той единицы, из которой я конвертирую.
Оказывается, синтаксис связывания функции Babel на самом деле не заботится о том, какой объект вы привязываете. Из-за этого вы можете предоставить свою собственную bind
функцию и включить любые функции, которые захотите.
Я добавил функцию bind
, которая возвращает миллисекунды, и внезапно ...
… Синтаксис стал еще короче и чище!
Метрик: буквально одна странная уловка
Это была комбинация подходов 2 и 3, которую я в конечном итоге опубликовал в моем пакете npm, Metrick. Я создал модуль duration
, который поддерживал время от миллисекунд до лет, а позже поддерживал data
, temperature
и length
единиц.
У Метрика есть классы для каждого типа юнитов. Metrick будет преобразовывать единицы измерения только между экземплярами одного и того же класса: вы не можете преобразовать hour
в celsius
, поскольку celsius
является экземпляром Temperature
, а не Duration
.
Для обеспечения единообразия каждый класс модуля расширяет базовый класс Unit
. Этот базовый класс обеспечивает фактическую функциональность; он отвечает за выполнение функций in
и bind
, а также за выполнение фактического преобразования единиц измерения.
Преимущество такой структуры в том, что она делает Metrick очень гибким!
Вы можете использовать встроенные блоки, а можете указать свои! Вы даже можете пойти дальше и создать новые типы юнита, если вам нужно работать в какой-то нише.
Языки не статичны
Люди по-прежнему пытаются развить сильные стороны JavaScript, исправить его слабые стороны и привнести в него новые идеи, и это действительно интересно.
Принимая мое разочарование по поводу магических чисел и применяя это к открытию чего-то интересного, я научился тому, что языки программирования (в частности, JavaScript) более гибкие, чем я думал.
Я думаю, что это действительно стоит того, чтобы исследовать что-то необычное, и я получил массу удовольствия от создания Метрика! Наши инструменты и языки зачастую более гибкие, чем мы думаем.