如何在JavaScript中处理货币值

by Sarah Dayan

通过莎拉·达扬

如何在JavaScript中处理货币值 (How to handle monetary values in JavaScript)

Money is everywhere.

金钱无处不在。

Banking apps, e-commerce websites, stock exchange platforms — we interact with money daily. We also increasingly rely on technology to handle ours.

银行应用程序,电子商务网站,证券交易平台-我们每天与金钱互动。 我们也越来越依靠技术来处理我们的技术。

Yet, there’s no consensus around how to programmatically handle monetary values. It’s a prevalent concept in modern societies, yet it’s not a first-class data type in any mainstream language, while things like date and time are. As a result, every piece of software comes up with its own way of handling money, with all the pitfalls that come with it.

但是,关于如何以编程方式处理货币价值尚无共识。 它是现代社会中流行的概念,但它不是任何主流语言中的一流数据类型,而日期和时间等则是。 结果, 每个软件都提出了自己的货币处理方式,并附带了所有陷阱

陷阱1:货币作为A N umber (Pitfall 1: Money as a Number)

Your first instinct when you need to represent money might be to use a Number.

当您需要代表金钱时,您的第一个本能可能是使用Number

Money is nothing more than a numeric value, right? Wrong.

金钱不过是数值,对不对? 错误。

The amount part of a monetary value is only relative to another aspect: its currency. There’s no such thing as 10 “money.” It’s 10 dollars, 10 euros, 10 bitcoins… If you want to add two monetary values with different currencies, you need to convert them first. Same if you want to compare them: if all you have is an amount, you can’t make an accurate comparison. Amount and currency can’t go without one another.

货币价值的金额部分仅与另一个方面有关:货币。 没有10“钱”这样的东西。 它是10美元,10欧元,10个比特币……如果您想将两个货币值和不同的货币相加,则需要先将其转换。 如果要比较它们,则相同:如果您所拥有的只是一个数量,就无法进行准确的比较。 数量和货币离不开彼此

陷阱2:浮点数学 (Pitfall 2: Floating point math)

Most contemporary currencies are either decimal or have no sub-units at all. This means that when money has sub-units, the number of these in a main unit is a power of 10. For example, there are 100 cents in a dollar, being 10 to the power of 2.

大多数当代货币要么是小数,要么根本没有子单位。 这意味着当货币具有子单位时,这些货币在主单位中的个数为10的幂。例如,一美元有100美分,即2的幂为10。

Using a decimal system has advantages, but raises a major issue when it comes to programming. Computers use a binary system, so they can’t natively represent decimal numbers. Some languages have come up with their own solutions like the BigDecimal type in Java or the decimal type in C#. JavaScript only has the Number type, which can be used as an integer or a double precision float. Because it’s a binary representation of a base 10 system, you end up with inaccurate results when you try to do math.

使用十进制系统有很多优点,但是在编程时会遇到一个主要问题。 计算机使用二进制系统,因此它们本身不能表示十进制数 。 某些语言已经提出了自己的解决方案,例如Java中的BigDecimal类型或C#中的decimal类型。 JavaScript仅具有Number类型,可以将其用作整数或双精度float 。 由于它是以10为基数的系统的二进制表示形式,因此当您尝试进行数学运算时,结果将不准确

0.1 + 0.2 // returns 0.30000000000000004 ?

Using floats to store monetary values is a bad idea.

用浮动货币来储存货币价值是个好主意

As you calculate more values, the imperceptible precision errors lead to larger gaps. This inevitably ends up causing rounding issues.

当您计算更多的值时,难以察觉的精度误差会导致更大的差距。 这不可避免地导致舍入问题。

陷阱3:百分比与分配 (Pitfall 3: Percentage vs. allocation)

Sometimes you need to split money, but percentages can’t cut it without adding or losing pennies.

有时,您需要拆分货币,但百分比不能在不增加或损失几分钱的情况下削减

Imagine you need to bill $999.99 with a 50% downpayment. This can be done with some simple math. Half is $499.995, but you can’t split a penny so you’ll likely round the result to $500. Problem is, as you charge the second half, you end up with the same result and charge a penny extra.

想象一下,您需要支付999.99美元并支付50%的首付。 这可以通过一些简单的数学来完成。 一半是499.995美元,但您不能分一分钱,因此可能会将结果舍入到500美元。 问题是,当您为下半部充电时,最终会得到相同的结果,并要多花一分钱。

You can’t solely rely on percentages or divisions to split money because it’s not divisible to infinity. Gas price may show more than two fraction digits, but it’s only symbolic: you always end up paying a rounded price.

您不能仅仅依靠百分比或除法来分割货币,因为它不能被无限除 。 汽油价格可能会显示两个以上的小数位数,但这仅是象征性的:您总是最终要支付一个四舍五入的价格。

工程救援 (Engineering to the rescue)

As you can see, there is much more to money than meets the eye, and it’s more than simple Number data types can take.

正如您所看到的,金钱所带来的收益远远超过了人们的视线,而且不仅仅是简单的Number数据类型可以接受的。

Fortunately, software engineer Martin Fowler came up with a solution. In Patterns of Enterprise Application Architecture, he describes a pattern for monetary values:

幸运的是, 软件工程师Martin Fowler提出了一个解决方案 。 在《企业应用程序体系结构的模式》中 ,他描述了货币价值的模式

物产 (Properties)

Methods

方法

  • Math: add, subtract, multiply, allocate

    数学:加,减,乘,分配
  • Comparison: equals to, greater than, greater than or equal, lesser than, lesser than or equal.

    比较:等于,大于,大于或等于,小于,小于或等于。

From this, you can create value objects that fulfill most of your monetary needs.

由此,您可以创建满足大多数货币需求的价值对象

金钱作为数据结构 (Money as a data structure)

Money behaves differently from a simple number, and thus should be treated differently. The first and most important thing is that it should always be composed of an amount and a currency.

金钱的行为与简单数字不同,因此应区别对待。 首先,也是最重要的一点是, 它应始终由数量和货币组成

You can do everything from an amount and a currency. You can add monetary amounts together, check if they’re equal or not, and format them into whatever you need. This can be done through an object’s methods. In JavaScript, any kind of function that returns an object will do the trick.

您可以使用金额和货币来做所有事情。 您可以将金额加在一起,检查它们是否相等,然后将其格式化为所需的格式。 这可以通过对象的方法来完成。 在JavaScript中,任何返回对象的函数都可以解决问题

分金额 (Amounts in cents)

There are several ways you can solve the floating point issue in JavaScript.

您可以通过多种方式解决JavaScript中的浮点问题。

You can use libraries like Decimal.js that will handle your floats as strings. This isn’t a bad solution, and even comes in handy when you have to handle big numbers. Yet, it comes at the expense of adding a (heavy) dependency, resulting in slower performance.

您可以使用像Decimal.js这样的库来将浮点数处理为字符串。 这不是一个糟糕的解决方案,甚至在您必须处理大数时也很方便。 但是,这是以增加(大量)依赖关系为代价的,从而导致性能降低

You can multiply floats into integers before you calculate, then divide them back.

您可以在计算之前将浮点数乘以整数,然后再除以整数。

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

It’s a fine solution, but requires extra calculations either on object construction or on each manipulation. This isn’t necessarily draining on performance, but is still more process work than necessary.

这是一个很好的解决方案,但是需要在对象构造或每次操作上进行额外的计算。 这并不一定会浪费性能,但是仍然需要进行更多的处理工作。

A third alternative is to directly store values in cents, relative to the unit. If you need to store 10 cents, you won’t store 0.1, but 10. This allows you to work with integers only, which means safe calculations (until you hit big numbers) and great performance.

第三种选择是相对于单位直接以美分存储值。 如果您需要存储10美分,则不会存储0.1 ,而是10 。 这使您只能使用整数,这意味着安全的计算(直到您击中大数 )和出色的性能。

Dinero.js,一个用于创建,计算和格式化货币值的不可变库 (Dinero.js, an immutable library to create, calculate, and format monetary values)

From these observations, I made a JavaScript library: Dinero.js.

根据这些观察,我制作了一个JavaScript库: Dinero.js

Dinero.js follows Fowler’s pattern and more. It lets you create, calculate, and format monetary values in JavaScript. You can do math, parse and format your objects, ask them questions, and make your development process easier.

Dinero.js遵循Fowler的模式等等。 它使您可以使用JavaScript创建,计算和格式化货币值。 您可以进行数学运算,解析和格式化对象,向他们提问,并使开发过程更轻松。

The library was designed to be immutable and chainable. It supports global settings, has extended formatting options, and provides native internationalization support.

该库被设计为不可变且可链接的。 它支持全局设置,具有扩展的格式选项,并提供本机国际化支持。

为什么一成不变? (Why immutable?)

An immutable library is safer and more predictable. Mutable operations and reference copies are the sources of many bugs. Opting for immutability avoids them altogether.

不变的库更安全,更可预测。 可变操作和参考副本是许多错误的根源。 选择不变性完全避免了它们。

With Dinero.js, you can perform calculations without worrying about altering original instances. In the following Vue.js example, price won’t be altered when priceWithTax is called. If the instance was mutable, it would.

使用Dinero.js,您可以执行计算而不必担心更改原始实例。 在以下Vue.js示例中,调用priceWithTax时不会更改price 。 如果实例是可变的,那么它将。

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

Good developers strive to make their code more concise and easier to read. When you want to successively perform several operations on a single object, chaining provides an elegant notation and concise syntax.

优秀的开发人员努力使他们的代码更简洁,更易于阅读。 当您要在单个对象上连续执行多个操作时,链接提供了一种优雅的表示法和简洁的语法。

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

When you’re handling lots of monetary values, chances are you want some of them to share some attributes. If you’re making a website in German, you’ll likely want to show amounts with the German currency format.

当您处理大量货币价值时,您可能希望其中一些共享某些属性。 如果您使用德语制作网站,则可能需要使用德语货币格式显示金额。

This is where global settings come in handy. Instead of passing them to every instance, you can declare options that will apply to all new objects.

这是全局设置派上用场的地方。 您可以声明将应用于所有新对象的选项,而不是将其传递给每个实例。

Dinero.globalLocale = 'de-DE'Dinero({ amount: 500 }).toFormat() // returns "5,00 $"
本机国际化支持 (Native Internationalization support)

Traditionally, libraries use locale files for internationalization. If you’re exhaustive, they tend to make libraries much heavier.

传统上,图书馆使用语言环境文件进行国际化。 如果您精疲力竭,它们往往会使库变得更重。

Locale files are also hard to maintain. The Internationalization API is native and pretty well supported. Unless you have to work with outdated and/or marginal browsers, toFormat is safe to use.

区域设置文件也很难维护。 国际化API是本机的, 并且受到很好的支持 。 除非您必须使用过时的和/或边际的浏览器, toFormat是可以安全使用的。

格式化 (Formatting)

An object is great to store data, but not so helpful when it comes to displaying it. Dinero.js comes with various formatting methods, including toFormat. It provides intuitive and concise syntactic sugar over Number.prototype.toLocaleString. Pair it with setLocale and you’ll be able to display any Dinero object into the proper format, in any language. This is particularly helpful for multi-lingual e-commerce websites.

对象很适合存储数据,但是在显示数据时却没有太大帮助。 Dinero.js带有多种格式化方法,包括toFormat 。 它在Number.prototype.toLocaleString上提供了直观简洁的语法糖。 将其与setLocale配对,您将能够以任何语言将任何Dinero对象显示为正确的格式。 这对于多语言电子商务网站特别有用。

下一步是什么? (What’s next?)

Fowler’s money pattern is widely recognized as a great solution. It has inspired many implementations in many languages. If you’re into DIY, I recommend it and the observations from this article as a starting point. Or you can pick Dinero.js: a modern, reliable, fully tested solution that already works.

福勒的货币模式被广泛认为是一个很好的解决方案。 它启发了许多语言的许多实现。 如果您对DIY感兴趣,我会推荐它以及本文的观察作为起点。 或者,您可以选择Dinero.js :一个已经有效的现代,可靠,经过全面测试的解决方案。

Have fun!

玩得开心!

Any questions about Dinero.js? Or on how to make your own money data structure? Let’s chat on Twitter!

对Dinero.js有任何疑问吗? 还是关于如何建立自己的货币数据结构? 让我们在Twitter上聊天!

Originally published at frontstuff.io.

最初发布在frontstuff.io上

翻译自: https://www.freecodecamp.org/news/how-to-handle-monetary-values-in-javascript-3fef5eeb3eda/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值