Как обрабатывать денежные значения в JavaScript

1656635050 kak obrabatyvat denezhnye znacheniya v javascript

автор Сара Даян

EHAJJiqkS2kjpiHtIT5R7U0XVCekveqHTwud
Фото Thought Catalog на Unsplash

Деньги повсюду.

Банковские программы, веб-сайты электронной коммерции, биржевые платформы – мы ежедневно взаимодействуем с деньгами. Мы также все больше полагаемся на технологии для обработки наших.

Однако нет единого мнения о том, как программно обрабатывать денежные значения. Это распространенная концепция в современных обществах, но это не первоклассный тип данных ни в одном из основных языков, тогда как такие вещи, как дата и время, есть. Как результат, каждая часть программного обеспечения предлагает свой собственный способ обращения с деньгами, со всеми ловушками, которые это связано.

Подводный камень №1: деньги как Нumber

Вашим первым инстинктом, когда вам нужно представить деньги, может быть использование a Number.

Деньги — это не что иное, как числовое значение, не правда ли? Неверно.

Суммовая часть денежной стоимости связана только с другим аспектом: ее валютой. Не существует такого понятия, как 10 «денег». Это 10 долларов, 10 евро, 10 биткойнов… Если вы хотите добавить две денежные величины с разными валютами, вам нужно сначала их конвертировать. То же самое, если вы хотите сравнить их: если все, что у вас есть, это сумма, вы не можете сделать точное сравнение. Сумма и валюта не могут идти друг без друга.

Подводный камень №2: математика с плавающей запятой

Большинство современных валют являются либо десятичными, либо вообще не имеют подразделений. Это означает, что когда деньги имеют субъединицы, их количество в главной единице составляет степень 10. Например, в долларе есть 100 центов, то есть 10 в степени 2.

Использование десятичной системы счисления имеет преимущества, но вызывает серьезную проблему, когда речь идет о программировании. Компьютеры используют двоичную систему, поэтому они не могут оригинально представить десятичные числа. Некоторые языки изобрели собственные решения, например BigDecimal введите Java или decimal введите C#. JavaScript имеет только Number тип, который может использоваться как целое или число с плавающей точкой двойной точности. Поскольку это двоичное представление системы с основанием 10, вы получаете неточные результаты, когда пытаетесь сделать математику.

0.1 + 0.2 // returns 0.30000000000000004 ? 

Использование плавающих значений для хранения денежных значений является плохой идеей.

Когда вы вычисляете больше значений, незаметные погрешности точности приводят к большим разрывам. Это неизбежно влечет за собой проблемы с округлением.

Подводный камень №3: процент против распределения

Иногда нужно делить деньги, но проценты не могут сократить это без добавления или потери пенни.

Представьте, что вам нужно выставить счет в размере 999,99 долларов с первоначальным взносом в размере 50%. Это можно сделать с помощью простой математики. Половина составляет 499 995 долларов, но вы не можете разделить ни копейки, так что вы, вероятно, округлите результат до 500 долларов. Проблема заключается в том, что когда вы заряжаете вторую половину, вы получаете тот же результат и взимаете пенни дополнительно.

Вы не можете полагаться только на проценты или деления, чтобы разделить деньги, поскольку оно не делится до бесконечности. Цена на газ может включать более двух цифр, но это лишь символично: вы всегда платите скругленную цену.

Техника в помощь

Как видите, деньги — это гораздо больше, чем кажется на первый взгляд, и это более чем просто Number типы данных могут приниматься.

к счастью Инженер-программист Мартин Фаулер предложил решение. в Шаблоны архитектуры корпоративных приложенийон описывает шаблон для денежных значений:

Свойства

Методы

  • Математика: добавить, вычесть, умножить, распределить
  • Сравнение: равно, больше, больше или равно, меньше, меньше или равно.

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

Деньги как структура данных

Деньги ведут себя иначе, чем обычное число, и потому к ним следует относиться иначе. Первое и самое важное это то он всегда должен состоять из суммы и валюты.

Вы можете сделать все, начиная от суммы и валюты. Вы можете составлять денежные суммы вместе, проверять, равны ли они, и форматировать их любым способом. Это можно сделать с помощью способов объекта. В JavaScript любая функция, возвращающая объект, сделает свое дело.

Сумы в центах

Существует несколько способов решения проблемы с плавающей запятой в JavaScript.

Вы можете использовать такие библиотеки как Decimal.js, которые будут обрабатывать ваши числа с плавающей точкой как строки. Это неплохое решение, которое даже пригодится, когда вам нужно обрабатывать большие числа. Тем не менее, это происходит за счет добавления (тяжелой) зависимости, что приводит к снижению производительности..

Вы можете умножить числа с плавающим веществом на цели перед вычислением, а затем разделить их обратно.

(0.2 * 100 + 0.01 * 100) / 100 // returns 0.21 ? 

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

Третья альтернатива состоит в том, чтобы непосредственно сохранять значение в центах по отношению к единице. Если вам нужно хранить 10 центов, вы не будете хранить 0.1но 10. Это позволяет вам работать только с целыми числами, что означает безопасные вычисления (пока вы не добьетесь больших чисел) и превосходную производительность.

Dinero.js, неизменная библиотека для создания, вычисления и форматирования денежных значений

На основе этих наблюдений я создал библиотеку JavaScript: Dinero.js.

4BaMJ7Ega3Xo2M3Iv5h3EiqqfsaLy7vA3HZB

Dinero.js следует шаблону Фаулера и т.д. Он позволяет создавать, рассчитывать и форматировать денежные значения в JavaScript. Вы можете заниматься математикой, анализировать и форматировать свои объекты, задавать им вопросы и облегчать процесс разработки.

Библиотека была разработана так, чтобы быть неизменной и цепной. Он поддерживает глобальные параметры, имеет расширенные параметры форматирования и обеспечивает поддержку интернационализации.

Почему неизменен?

Неизменная библиотека более безопасна и предсказуема. Сменные операции и эталонные копии являются источниками многих ошибок. Выбор неизменности позволяет избежать их вообще.

С помощью Dinero.js вы можете производить вычисления, не беспокоясь об изменении исходных экземпляров. В приведенном ниже примере Vue.js price не будет изменен, когда priceWithTax это называется. Если бы экземпляр был переменным, он был бы.

const vm = new Vue({  data: {    price: Dinero({ amount: 500 })  },  computed: {    priceWithTax() {      return this.price.add(this.price.percentage(10))    }  }})

Концепция

Хорошие разработчики стремятся сделать свой код более лаконичным и лёгким для чтения. Если вы хотите последовательно выполнить несколько операций над одним объектом, цепочка обеспечивает элегантное обозначение и сжатый синтаксис.

Dinero({ amount: 500 })  .add(Dinero({ amount: 200 }))  .multiply(4)  .setLocale('fr-FR')  .toFormat() // returns "28,00 US$"

Глобальные настройки

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

Здесь пригодятся глобальные настройки. Вместо того, чтобы передавать их каждому экземпляру, можно объявить параметры, которые будут применяться ко всем новым объектам.

Dinero.globalLocale="de-DE"Dinero({ amount: 500 }).toFormat() // returns "5,00 $"

Внутренняя поддержка интернационализации

Традиционно в библиотеках используются файлы локали для интернационализации. Если вы исчерпывающие, они, как правило, делают библиотеки гораздо труднее.

4l3nTXKehx5SpPmPpl18QQXnfS2d4WE-MpAY
Moment.js в четыре раза тяжелее файлов локали.

Локальные файлы тоже трудно поддерживать. API интернационализации является родным и достаточно хорошо поддерживается. Если вам не придется работать с устаревшими и/или маргинальными браузерами, toFormat безопасен в использовании.

Форматирование

Объект отлично подходит для хранения данных, но не столь полезен, когда дело доходит до его отображения. Dinero.js поставляется с разными методами форматирования, в частности toFormat. Он обеспечивает интуитивно понятный и сжатый синтаксический сахар Number.prototype.toLocaleString. Соедините его с setLocale и вы сможете отобразить любой объект Dinero в надлежащем формате на любом языке. Это особенно полезно для многоязычных веб-сайтов электронной коммерции.

Что дальше?

Денежный шаблон Фаулера широко признан замечательным решением. Это вдохновило много реализаций на многих языках. Если вы любите DIY, я рекомендую это и наблюдение из этой статьи в качестве отправной точки. Или вы можете выбрать Dinero.js: современное, надежное, полностью проверенное уже работающее решение.

Веселитесь!

Есть вопросы по Dinero.js? Или о том, как заработать свою структуру данных денег? Давайте общаться в Twitter!

Первоначально опубликовано на frontstuff.io.

Добавить комментарий

Ваш адрес email не будет опубликован.