Долгое время я восхищался языками программирования, которые пытаются имитировать естественные языки, от которых они произошли. Я часто ищу более естественные способы выражения концепций в программировании.

Как человек, который потратил много времени на работу с довольно унылой встроенной функциональностью JavaScript, я нашел в Ruby (или, точнее, Rails) довольно приятным моментом Числовые расширения ActiveSupport. 3.hours.ago синтаксис ActiveSupport более похож на английский, чем, например, Moment.jsmoment().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) более гибкие, чем я думал.

Я думаю, что это действительно стоит того, чтобы исследовать что-то необычное, и я получил массу удовольствия от создания Метрика! Наши инструменты и языки зачастую более гибкие, чем мы думаем.