如何处理JavaScript 中的货币值?

金钱无处不在。

  无论在银行应用程序、电子商务网站还是证券交易所平台,我们每天都在与金钱互动。我们也越来越依赖技术来处理问题。

  然而,关于如何以编程处理货币价值尚无共识。虽然金钱是现代社会中普遍存在的概念,但相较于日期和时间之类的东西,它并不是任何主流语言中的一流数据类型。结果,每一种软件都有自己的处理方式,且伴随着陷阱。

  陷阱#1:金钱是数字?

  当你需要代表钱时,你的第一直觉可能是使用一个数字。

  金钱只不过是一个数值,对吧?

  错了。

  货币价值的一部分与另一对象有关:货币。没有10“钱”,应该是“10美元,10欧元,10比特币”......如果你想用不同的货币添加两个货币值,你需要先转换它们。如果你想比较它们也是如此:如果你只有一个金额,你就无法进行准确的比较。金额和货币谁也离不开谁。

  陷阱#2:让人烦恼的小数点

  大多数现代货币额都是以小数形式出现,或根本没有子单位。这意味着当货币有子单位时,主单位中这些单位的数量是10的幂。例如,一美元有100美分——10的2次幂。

  使用十进制系统具有优势,但在编程方面有一个问题。计算机使用二进制系统,因此它们不能原生地表示十进制数。有些语言提出了自己的解决方案,如Java中的BigDecimal类型或C#中的小数类型。JavaScript只有Number类型,可以用作整数或双精度浮点数。因为它是基础10系统的二进制表示,所以当你尝试进行数学运算时,最终会得到不准确的结果。

  使用浮点来存储货币价值是一个坏主意。

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

  陷阱#3:百分比与分期

  有时你需要计算分期,但百分比总是或增加或减少付款数。

  想象一下,你需要支付999.99美元,首付50%。这可以通过一些简单的数学来完成。一半是499.995美元,但是你不能再分一分钱,所以你可能会把结果分成500美元。问题是,当你付尾款时,你最终会获得相同的结果并且额外付一分钱。

  分期有时候会遇到除不尽的情况。天然气价格可能显示超过两位数,但它只是象征性的:你最终总是支付一个四舍五入的价格。

  编程人员应该怎么办?

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

  属性

  方法

  数学:加,减,乘,除

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

  由此,你可以创建满足大部分货币需求的价值对象。

  “金额+货币”作为数据结构

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

  你可以将货币金额一起添加,检查它们的值是否相对应,并将它们格式化为你需要的任何内容。这可以通过对象的方法完成。在JavaScript中,任何返回对象的函数都可以解决问题。

  以美分计额

  有几种方法可以解决JavaScript中的浮点问题。

  你可以使用像Decimal.js这样的库来将浮点数作为字符串。这不是一个糟糕的解决方案,当你必须处理大数字时,它会派上用场。然而,它以增重依赖性为代价,导致性能降低。

  你可以在计算之前将浮点数乘以整数,然后将它们分开。

  

  这是一个很好的解决方案,但需要在对象构造或每次操作时进行额外的计算。这不一定会影响性能,但仍然需要更多的流程工作。

  第三种方法是直接以美分为单位存储相对于单位的值。如果你需要存储10美分,则不会存储0.1,而是10.这允许你仅使用整数,这意味着安全计算(直到你遇到大数字)和出色的性能。

  Dinero.js:一个用于创建、计算和格式化货币价值的不可变库

  从以上观察中,我创建了一个JavaScript库:Dinero.js。

  

  Dinero.js遵循Fowler的模式更多一点儿。它允许你在JavaScript中创建、计算和格式化货币值。你可以进行数学运算、解析和格式化对象,甚至向他们提问,使你的开发过程更加轻松。

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

  为什么不可变?

  不可变库更安全,更好预测。可变操作和引用副本是许多错误的来源。选择不变性能够避免了这些错误。

  使用Dinero.js,你可以执行计算而无需担心更改原始用例。在以下Vue.js示例中,调用priceWithTax时不会更改Price。如果用例是可变的,它将会更改价格。

  

  可链接性

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

  

  全球设置

  当你处理大量货币价值时,你可能希望其中一些人分享一些属性。如果你使用德语制作网站,你可能希望以德国货币格式显示金额。

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

  

  原生国际化支持

  传统意义上,库使用区域设置文件进行国际化。

  

  区域设置文件也很难维护。Internationalization API是原生的,并且得到了很不错的支持。除非你必须使用过时的或不知名的浏览器,否则toFormat可以安全使用。

  格式化

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

  接下来做什么?

  大家广泛认同Fowler模式是一个不错的解决方案。它激发了许多语言的同步实现。如果你正在DIY,我推荐它和本文的观察结果作为起点。或者你可以选择Dinero.js:一种现代,可靠,经过全面测试的解决方案,已经可以使用。

原文地址:http://blog.51cto.com/13957185/2316818

时间: 2024-11-05 17:34:09

如何处理JavaScript 中的货币值?的相关文章

JavaScript中的内存泄漏以及如何处理

随着现在的编程语言功能越来越成熟.复杂,内存管理也容易被大家忽略.本文将会讨论JavaScript中的内存泄漏以及如何处理,方便大家在使用JavaScript编码时,更好的应对内存泄漏带来的问题. 概述 像C语言这样的编程语言,具有简单的内存管理功能函数,例如malloc( )和free( ).开发人员可以使用这些功能函数来显式地分配和释放系统的内存. 当创建对象和字符串等时,JavaScript就会分配内存,并在不再使用时自动释放内存,这种机制被称为垃圾收集.这种释放资源看似是"自动"

javascript中的属性类型

ECMA-262第5版在定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特性.ECMA-262定义这些特性是为了实现javascript引擎用的,因此在javascript中不能直接访问它们.为了表示特性是内部值,该规范把它们放在了两对方括号中,例如[[Enumerable]]. ECMAScript中有两种属性:数据属性和访问器属性. 1.数据属性包含一个数据值的位置.在这个位置可以读取和写入值.数据属性有4个描述其行为的特性. [[Configurable

解析JavaScript中apply和call以及bind

函数调用方法 在谈论JavaScript中apply.call和bind这三兄弟之前,我想先说下,函数的调用方式有哪些: 作为函数 作为方法 作为构造函数 通过它们的call()和apply()方法间接调用 前面的三种调用方法,我们都知道且不在这篇文章的讨论范围内,就不说了. 下面我们来说说这第四种调用方法 通过call()和apply()间接调用 其实,我们可以将这两个函数看做是某个对象的方法,通过调用方法的方式来间接调用函数: function f(){} f.call(o); f.appl

Javascript中的浅拷贝和深拷贝

很多开发语言中都有浅拷贝和深拷贝的说法,这里简单区分一下它们在Javascript中的区别,以及jQuery中深拷贝的实现. 在谈浅拷贝和深拷贝之前,先要屡清楚Javascript中的按值访问和按引用访问这两个概念. 按值访问是针对基本类型(string.number.boolean.null.undefined).基本类型以值的形式被存放在栈内存中,我们通过变量操作的是栈内存中实际的值. 按引用访问时针对引用类型(Object.Array.Date.RegExp.Function).引用类型以

理解javascript中的回调函数(callback)【转】

在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实际上是一种对象,它可以"存储在变量中,通过参数传递给(别一个)函数(function),在函数内部创建,从函数中返回结果值". 因为function是内置对象,我们可以将它作为参数传递给另一个函数,延迟到函数中执行,甚至执行后将它返回.这是在JavaScript中使用回调函数的精髓.本篇文

Java数字、货币值和百分数等的格式化处理

如果我们用下列语句输出一个数 System.out.println(123456.789); 将会在Console看到输出 123456.789 那么如何得到123,456.789这种格式化的输出呢?这里就需要用到java.text.Format这个类.不仅是数字,它还提供了货币值和百分数的格式化输出,比如0.58的百分数输出形式是58%.要获得本地的默认格式,可以用下列方法获得 Java代码   NumberFormat.getNumberInstance() NumberFormat.get

实现一个函数clone,使JavaScript中的5种主要的数据类型(包括Number、String、Object、Array、Boolean)进行值复制

实现一个函数clone,可以对JavaScript中的5种主要的数据类型(包括Number.String.Object.Array.Boolean)进行值复制. 1 /** 对象克隆 2 * 支持基本数据类型及对象 3 * 递归方法 */ 4 function clone(obj) { 5 var o; 6 switch (typeof obj) { 7 case "undefined": 8 break; 9 case "string": o = obj + &q

javascript中的原始值和复杂值

前面的话 javascript的数据类型可以分为两种:原始类型(基本类型或者简单类型)和引用类型. 原始类型:Undefined,Null,Boolean,Number,String五种: 引用类型:Object,Array,Function: 与此相对应的,它们的值分别被称为原始值和复杂值. 特性 原始值 原始值是表示javascript中可用的数据或信息的最底层的形式或者最简单的形式.原始类型的值被称为原始值,因为它们的值是不可被细化的.也就是说,数字是数字,字符串是字符串,布尔值是true

【JS】JavaScript中的执行环境与作用域

JavaScript中的执行环境定义了变量或函数有权访问的数据(每个函数都有自己的执行环境),全局执行环境是最外围的执行环境,在浏览器中,全局执行环境就是window对象,所以所有的全局变量和函数都是作为window对象的属性和方法创建的.当某一个执行环境中所有代码执行完成后,该环境就被销毁,保存在其中的变量和函数也将被销毁,全局执行环境在关闭网页或浏览器时才被销毁. 当代码在一个环境中执行时,会创建变量对象的一个作用域链(保证对执行环境有权访问的变量和函数的有序访问),如果环境是函数,将其活动