题 如何在JavaScript中将数字格式化为美元货币字符串?


我想用JavaScript格式化价格。
我想要一个需要的功能 float 作为一个参数并返回一个 string 格式如下:

"$ 2,500.00"

最好的方法是什么?


1390


起源


没有内置功能 formatNumber 在javascript中 - zerkms
对于将来读这篇文章的人,请做 不 使用浮动来存储货币。您将丢失精度和数据。您应该将其存储为整数个美分(或便士等),然后在输出之前进行转换。 - Philip Whitehouse
@ user1308743 Float不存储小数位。它使用值,基数和偏移量存储数字。 0.01实际上不可表示。看到: en.wikipedia.org/wiki/Floating_point#Accuracy_problems - Philip Whitehouse
@ user1308743:想象一下,你代表了一个非常大的数字(假设你是一个幸运的人,这是你的银行账户余额)。你真的想因为精确缺乏而放松钱吗? - ereOn
那么为什么没有人提出以下建议呢? (2500).toLocaleString(“en-GB”,{style:“currency”,currency:“GBP”,minimumFractionDigits:2}) developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... - Nick Grealy


答案:


您可以使用:

  var profits=2489.8237
  profits.toFixed(3) //returns 2489.824 (round up)
  profits.toFixed(2) //returns 2489.82
  profits.toFixed(7) //returns 2489.8237000 (padding)

然后你可以添加'$'的标志。

如果你需要','千万你可以使用:

Number.prototype.formatMoney = function(c, d, t){
    var n = this, 
    c = isNaN(c = Math.abs(c)) ? 2 : c, 
    d = d == undefined ? "." : d, 
    t = t == undefined ? "," : t, 
    s = n < 0 ? "-" : "", 
    i = String(parseInt(n = Math.abs(Number(n) || 0).toFixed(c))), 
    j = (j = i.length) > 3 ? j % 3 : 0;
   return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
 };

并使用它:

(123456789.12345).formatMoney(2, '.', ',');

如果你总是要使用'。'和',',你可以让他们离开你的方法调用,该方法将为你默认它们。

(123456789.12345).formatMoney(2);

如果您的文化中有两个符号翻转(即欧洲人),只需粘贴以下两行即可 formatMoney 方法:

    d = d == undefined ? "," : d, 
    t = t == undefined ? "." : t, 

1516



首先,优秀,简洁的代码。但是,如果您是美国人,则应更改默认值 d 和 t 成为 . 和 , 分别使你不必每次都指定它们。另外,我建议修改开头 return 声明: return s + '$' + [rest],否则你不会得到一个美元符号。 - Jason
不确定为什么人们认为这段代码很漂亮。这是难以理解的。它似乎工作得很好,但它并不美观。 - usr
这个formatMoney函数是从某些缩小的JavaScript代码复制的吗?你能不发布原文?变量c,d,i,j,n,s和t代表什么?根据这篇文章的评价和评论的数量来判断,我可以假设这个代码已经被复制粘贴到各个生产网站上...如果有一天它有错误,祝你好运! - zuallauz
“诗歌”?更像是默默无闻。这不是代码高尔夫;使用一点白色空间。适当的var名称也不会受到伤害。 - keithjgrant
任何傻瓜都可以编写计算机可以理解的代码。优秀的程序员编写人类可以理解的代码 - Liam


简短快速的解决方案(无处不在!)

(12345.67).toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,');  // 12,345.67

这个解决方案背后的想法是用第一个匹配和逗号替换匹配的部分,即 '$&,'。匹配是使用完成的 前瞻性的方法。您可以将表达式读作 “匹配一个数字,如果它后跟一个三个数字集(一个或多个)和一个点的序列”

测试:

1        --> "1.00"
12       --> "12.00"
123      --> "123.00"
1234     --> "1,234.00"
12345    --> "12,345.00"
123456   --> "123,456.00"
1234567  --> "1,234,567.00"
12345.67 --> "12,345.67"

DEMO:  http://jsfiddle.net/hAfMM/9571/


扩展的短解决方案

你也可以扩展原型 Number 对象添加任意数量的小数的附加支持 [0 .. n] 和数字组的大小 [0 .. x]

/**
 * Number.prototype.format(n, x)
 * 
 * @param integer n: length of decimal
 * @param integer x: length of sections
 */
Number.prototype.format = function(n, x) {
    var re = '\\d(?=(\\d{' + (x || 3) + '})+' + (n > 0 ? '\\.' : '$') + ')';
    return this.toFixed(Math.max(0, ~~n)).replace(new RegExp(re, 'g'), '$&,');
};

1234..format();           // "1,234"
12345..format(2);         // "12,345.00"
123456.7.format(3, 2);    // "12,34,56.700"
123456.789.format(2, 4);  // "12,3456.79"

演示/测试:  http://jsfiddle.net/hAfMM/435/


超级扩展的短解决方案

在这 超级扩展版 您可以设置不同的分隔符类型:

/**
 * Number.prototype.format(n, x, s, c)
 * 
 * @param integer n: length of decimal
 * @param integer x: length of whole part
 * @param mixed   s: sections delimiter
 * @param mixed   c: decimal delimiter
 */
Number.prototype.format = function(n, x, s, c) {
    var re = '\\d(?=(\\d{' + (x || 3) + '})+' + (n > 0 ? '\\D' : '$') + ')',
        num = this.toFixed(Math.max(0, ~~n));

    return (c ? num.replace('.', c) : num).replace(new RegExp(re, 'g'), '$&' + (s || ','));
};

12345678.9.format(2, 3, '.', ',');  // "12.345.678,90"
123456.789.format(4, 4, ' ', ':');  // "12 3456:7890"
12345678.9.format(0, 3, '-');       // "12-345-679"

演示/测试:  http://jsfiddle.net/hAfMM/612/


1022



我实际上更进了一步: .replace(/(\d)(?=(\d{3})+(?:\.\d+)?$)/g, "$1,")。 - kalisjoshua
使用VisioN和kalisjoshua regexp的CoffeeScript版本以及指定小数位的方式(因此您可以保留默认值2或指定0表示无小数): Number.prototype.toMoney = (decimal=2) -> @toFixed(decimal).replace /(\d)(?=(\d{3})+(?:\.\d+)?$)/g, "$1," - Eric Anderson
@Abbas是的,替换 \. 同 $ (行尾),即 this.toFixed(0).replace(/(\d)(?=(\d{3})+$)/g, "$1,")。 - VisioN
@hanumant这里的常规语法有点复杂,所以我建议你先阅读有关正则表达式的手册(例如at MDN)。它背后的想法是用第一个匹配和逗号替换匹配的部分,即 $1,。匹配是使用完成的 前瞻性的方法。您可以将表达式读作 “匹配一个数字,如果它后跟一个三个数字集(一个或多个)和一个点的序列”。 - VisioN
@JuliendePrabère请举一个长数字的例子,这个方法不适用。 - VisioN


Intl.numberformat

Javascript有一个数字格式化程序,它是Internationalization API的一部分。

// Create our number formatter.
var formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  minimumFractionDigits: 2,
  // the default value for minimumFractionDigits depends on the currency
  // and is usually already 2
});

formatter.format(2500); /* $2,500.00 */

JS小提琴

关于浏览器支持的一些注意

  • 所有主流浏览器 现在支持它
  • 支持方面最大的违法者是UC Mobile(远离那个)和Opera Mini(设计残缺)
  • 有一个 垫片 在旧版浏览器上支持它
  • 看一下 我可以用吗 了解更多信息

Intl.NumberFormat与Number.prototype.toLocaleString

最后一点,将其与较旧版本进行比较。toLocaleString。它们都提供基本相同的功能。但是,toLocaleString在它的旧版本中(pre-Intl) 实际上并不支持区域设置:它使用系统区域设置。因此,为了确保您使用的是正确的版本, MDN建议检查是否存在 Intl。所以如果你需要检查Intl,为什么不使用  代替? 但是,如果您选择使用垫片,那也会补丁 toLocaleString,所以在这种情况下你可以毫无困难地使用它:

(2500).toLocaleString('en-US', {
  style: 'currency',
  currency: 'USD',
}); /* $2,500.00 */

761



这个idomatic JavaScript,简单而优雅的解决方案正是我所寻求的。 - Guilhem Soulas
野生动物园不可靠 - chulian
我喜欢这个,但在使用之前请检查支持: caniuse.com/#feat=internationalization - jocull
投票给这个,因为这是一个简单的回答,本身就是有效的。 - Trasiva
很确定很高比例的浏览器现在支持这一点。这应该得到更多的支持。 - flq


看看JavaScript  对象,看看它是否可以帮助你。

  • toLocaleString() 将使用特定于位置的千位分隔符格式化数字。
  • toFixed() 将数字四舍五入到特定的小数位数。

要同时使用这些值,必须将其类型更改回数字,因为它们都输出一个字符串。

例:

Number(someNumber.toFixed(1)).toLocaleString()

160



谢谢!基于这个想法,我能够制作一个简短而简单的产品! (和本地化)优秀。 - Daniel Magliola
其实你可以。即对于美元:'$'+(值+ 0.001)。toLocaleString()。slice(0,-1) - Zaptree
看起来它很棒,但目前几乎没有浏览器支持 - acorncom
@acorncom为什么你说“浏览器支持很少”?自Javascript 1.1以来,Number对象一直存在。请提供支持您的索赔的参考。 - Doug S
应该注意有一个旧版本 toLocaleString 它使用系统区域设置,以及来自ECMAScript Intl API的新(不兼容)区域设置。 这里解释一下。这个答案似乎是针对旧版本的。 - aross


下面是 Patrick Desjardins(别名Daok) 添加了一些注释的代码和一些小的更改:

/* 
decimal_sep: character used as deciaml separtor, it defaults to '.' when omitted
thousands_sep: char used as thousands separator, it defaults to ',' when omitted
*/
Number.prototype.toMoney = function(decimals, decimal_sep, thousands_sep)
{ 
   var n = this,
   c = isNaN(decimals) ? 2 : Math.abs(decimals), //if decimal is zero we must take it, it means user does not want to show any decimal
   d = decimal_sep || '.', //if no decimal separator is passed we use the dot as default decimal separator (we MUST use a decimal separator)

   /*
   according to [https://stackoverflow.com/questions/411352/how-best-to-determine-if-an-argument-is-not-sent-to-the-javascript-function]
   the fastest way to check for not defined parameter is to use typeof value === 'undefined' 
   rather than doing value === undefined.
   */   
   t = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep, //if you don't want to use a thousands separator you can pass empty string as thousands_sep value

   sign = (n < 0) ? '-' : '',

   //extracting the absolute value of the integer part of the number and converting to string
   i = parseInt(n = Math.abs(n).toFixed(c)) + '', 

   j = ((j = i.length) > 3) ? j % 3 : 0; 
   return sign + (j ? i.substr(0, j) + t : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : ''); 
}

这里有一些测试:

//some tests (do not forget parenthesis when using negative numbers and number with no decimals)
alert(123456789.67392.toMoney() + '\n' + 123456789.67392.toMoney(3) + '\n' + 123456789.67392.toMoney(0) + '\n' + (123456).toMoney() + '\n' + (123456).toMoney(0) + '\n' + 89.67392.toMoney() + '\n' + (89).toMoney());

//some tests (do not forget parenthesis when using negative numbers and number with no decimals)
alert((-123456789.67392).toMoney() + '\n' + (-123456789.67392).toMoney(-3));

微小的变化是:

  1. 感动了一下 Math.abs(decimals) 只有在没有的时候才能完成 NaN

  2. decimal_sep 不能再为空字符串(必须使用某种小数分隔符)

  3. 我们用 typeof thousands_sep === 'undefined' 如建议的那样 如何最好地确定参数是否未发送到JavaScript函数

  4. (+n || 0) 不需要因为 this 是一个 Number 目的


157



您可能希望在parseInt中使用“10”作为基数。否则,任何以“0”开头的数字都将使用八进制编号。 - sohtimsso1970
@ sohtimsso1970:对于迟到的回复感到抱歉,但你能解释一下吗?我没有看到数字可以解释为八进制的位置。该 parseInt 调用INTEGER部分的绝对值。 INTEGER部分不能以ZERO开头,除非它只是一个零!和 parseInt(0) === 0 八进制或十进制。 - Marco Demaio
尝试,例如:parseInt(“016”)...返回14,因为当字符串以零开头时,parseInt假定它是八进制编码的。 - Tracker1
@ Tracker1:我明白了一个以数字开头的数字 0 被认为是八进制的 parseInt。但是在这段代码中是不可能的 parseInt 受到 016 作为输入(或任何其他八进制格式化的值),因为参数传递给 parseInt 是第一次处理 Math.abs 功能。所以没有办法 parseInt 接收一个以零开头的数字,除非它只是零或 0.nn (哪里 nn 是小数)。但两者都有 0 和 0.nn 字符串将被转换 parseInt 成为一个普通的ZERO。 - Marco Demaio
此函数不正确:>(2030).toMoney(0,'。',''); <“2 03 0” - Anton P Robul


accounting.js 是一个很小的JavaScript库,用于数字,金钱和货币格式。


120



...只记得传递一个货币符号,否则它在IE7和IE8中出错,IE9无论哪种方式都很好 - vector
看起来修复了IE7 / IE8错误。 - Mat Schaffer
这是一个很棒的图书馆,能够通过货币符号也是一个好主意,因为所有的货币细节都包含在单个函数调用/设置中 - farinspace
我喜欢你可以反过来的事实 - 传递格式化的货币字符串并获取数值。 - Neil Monroe
accounting.js最近似乎没有得到维护。最新变化的一个分支是 github.com/nashdot/accounting-js - RationalDev


这是我见过的最好的js money formatter:

Number.prototype.formatMoney = function(decPlaces, thouSeparator, decSeparator) {
    var n = this,
        decPlaces = isNaN(decPlaces = Math.abs(decPlaces)) ? 2 : decPlaces,
        decSeparator = decSeparator == undefined ? "." : decSeparator,
        thouSeparator = thouSeparator == undefined ? "," : thouSeparator,
        sign = n < 0 ? "-" : "",
        i = parseInt(n = Math.abs(+n || 0).toFixed(decPlaces)) + "",
        j = (j = i.length) > 3 ? j % 3 : 0;
    return sign + (j ? i.substr(0, j) + thouSeparator : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thouSeparator) + (decPlaces ? decSeparator + Math.abs(n - i).toFixed(decPlaces).slice(2) : "");
};

它被重新格式化并从这里借来: https://stackoverflow.com/a/149099/751484

你必须提供自己的货币代号(你使用上面的$)。

像这样调用它(虽然注意args默认为2,逗号和句点,所以如果这是你的偏好,你不需要提供任何args):

var myMoney=3543.75873;
var formattedMoney = '$' + myMoney.formatMoney(2,',','.'); // "$3,543.76"

95



注意全局标志,我,j - hacklikecrack
@hacklikecrack,所有变量都是本地的;他们在 var 声明。 - Jonathan M
对不起,是的,虽然您正在重新声明参数。缩进! ;) - hacklikecrack
变量名称的可怕使用! - Serj Sagan