【问题标题】:How to format numbers as currency strings如何将数字格式化为货币字符串
【发布时间】:2012-03-08 06:54:05
【问题描述】:

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

"$ 2,500.00"

最好的方法是什么?

【问题讨论】:

  • 请各位以后阅读本文的人不要使用浮点数来存储货币。您将失去精度和数据。您应该将其存储为整数美分(或便士等),然后在输出之前进行转换。
  • @user1308743 浮点数不存储小数位。它使用值、基数和偏移量存储数字。 0.01 实际上无法表示。见:en.wikipedia.org/wiki/Floating_point#Accuracy_problems
  • @user1308743:假设你代表一个非常大的数字(假设你是一个幸运的人,这是你的银行账户余额)。你真的会因为精度不足而亏钱吗?
  • 那么为什么没有人提出以下建议? (2500).toLocaleString("en-GB", {style: "currency", currency: "GBP", minimumFractionDigits: 2}) developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • 您可以使用这个 Numeral.js javascript 库将您的数字转换为美元。 (numeraljs.com) 供参考。

标签: javascript formatting currency


【解决方案1】:

Intl.NumberFormat

JavaScript 有一个数字格式化程序(国际化 API 的一部分)。

// Create our number formatter.
var formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',

  // These options are needed to round to whole numbers if that's what you want.
  //minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1)
  //maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501)
});

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

使用undefined 代替第一个参数(示例中为'en-US')以使用系统区域设置(如果代码在浏览器中运行,则为用户区域设置)。 Further explanation of the locale code.

这是list of the currency codes

Intl.NumberFormat 与 Number.prototype.toLocaleString

最后一个注释,将其与旧的 .toLocaleString 进行比较。它们都提供基本相同的功能。但是,toLocaleString 在其较旧的化身(pre-Intl)does not actually support locales:它使用系统语言环境。因此,在调试旧浏览器时,请确保您使用的是正确的版本 (MDN suggests to check for the existence of Intl)。如果您不关心旧浏览器或只使用shim,则根本无需担心这一点。

此外,对于一个单个项,两者的性能是相同的,但如果您有很多数字要格式化,使用Intl.NumberFormat 会快约70 倍。因此,通常最好使用Intl.NumberFormat 并在每次页面加载时仅实例化一次。无论如何,这是toLocaleString的等效用法:

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

关于浏览器支持和 Node.js 的一些说明

  • 浏览器支持如今已不再是问题,全球支持率为 98%,美国为 99%,欧盟为 99% 以上
  • 如果你真的需要的话,有一个 shim 可以在僵化的浏览器上支持它(例如 Internet Explorer 8
  • v13 之前的 Node.js 仅支持 en-US 开箱即用。一种解决方案是安装full-icu,有关详细信息,请参阅here
  • 查看CanIUse 了解更多信息

【讨论】:

  • 投票这个是因为它是一个愚蠢的简单答案,可以在本地工作。
  • 相当高的浏览器现在支持这个。这应该得到更多的支持。
  • 这是一个很好的答案,我让它使用动态货币值,所以如果在欧洲使用,那么它会更改为 EUR 并显示欧元符号。工作一种享受!
  • 现在是 2018 年,基本上到处都支持。这应该是正确的答案。
  • 支持早至 Internet Explorer 11 (IE 11),所有主流浏览器也都支持。
【解决方案2】:

Number.prototype.toFixed

此解决方案兼容所有主流浏览器:

  const profits = 2489.8237;

  profits.toFixed(3) // Returns 2489.824 (rounds up)
  profits.toFixed(2) // Returns 2489.82
  profits.toFixed(7) // Returns 2489.8237000 (pads the decimals)

您只需添加货币符号(例如"$" + profits.toFixed(2)),您将获得美元金额。

自定义函数

如果你需要在每个数字之间使用,,你可以使用这个函数:

function formatMoney(number, decPlaces, decSep, thouSep) {
    decPlaces = isNaN(decPlaces = Math.abs(decPlaces)) ? 2 : decPlaces,
    decSep = typeof decSep === "undefined" ? "." : decSep;
    thouSep = typeof thouSep === "undefined" ? "," : thouSep;
    var sign = number < 0 ? "-" : "";
    var i = String(parseInt(number = Math.abs(Number(number) || 0).toFixed(decPlaces)));
    var j = (j = i.length) > 3 ? j % 3 : 0;

    return sign +
        (j ? i.substr(0, j) + thouSep : "") +
        i.substr(j).replace(/(\decSep{3})(?=\decSep)/g, "$1" + thouSep) +
        (decPlaces ? decSep + Math.abs(number - i).toFixed(decPlaces).slice(2) : "");
}

document.getElementById("b").addEventListener("click", event => {
  document.getElementById("x").innerText = "Result was: " + formatMoney(document.getElementById("d").value);
});
<label>Insert your amount: <input id="d" type="text" placeholder="Cash amount" /></label>
<br />
<button id="b">Get Output</button>
<p id="x">(press button to get output)</p>

像这样使用它:

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

如果你总是要使用 '.'和 ',',您可以将它们从您的方法调用中去掉,该方法将为您默认它们。

(123456789.12345).formatMoney(2);

如果您的文化有两个符号翻转(即欧洲人)并且您想使用默认值,只需将以下两行粘贴到 formatMoney 方法中:

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

自定义函数(ES6)

如果你可以使用现代 ECMAScript 语法(即通过 Babel),你可以使用这个更简单的函数来代替:

function formatMoney(amount, decimalCount = 2, decimal = ".", thousands = ",") {
  try {
    decimalCount = Math.abs(decimalCount);
    decimalCount = isNaN(decimalCount) ? 2 : decimalCount;

    const negativeSign = amount < 0 ? "-" : "";

    let i = parseInt(amount = Math.abs(Number(amount) || 0).toFixed(decimalCount)).toString();
    let j = (i.length > 3) ? i.length % 3 : 0;

    return
      negativeSign +
      (j ? i.substr(0, j) + thousands : '') +
      i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousands) +
      (decimalCount ? decimal + Math.abs(amount - i).toFixed(decimalCount).slice(2) : "");
  } catch (e) {
    console.log(e)
  }
};

document.getElementById("b").addEventListener("click", event => {
  document.getElementById("x").innerText = "Result was: " + formatMoney(document.getElementById("d").value);
});
<label>Insert your amount: <input id="d" type="text" placeholder="Cash amount" /></label>
<br />
<button id="b">Get Output</button>
<p id="x">(press button to get output)</p>

【讨论】:

  • 首先,优秀、简洁的代码。但是,如果您是美国人,则应将dt 的默认值分别更改为.,,这样您就不必每次都指定它们。另外,我建议将return 语句的开头修改为:return s + '$' + [rest],否则你不会得到美元符号。
  • 不知道为什么人们认为这段代码很漂亮。这是无法解读的。它似乎工作得很好,但它并不漂亮。
  • 这个 formatMoney 函数是从某个地方的一些缩小的 JavaScript 代码中复制而来的吗?不能发原文吗?变量 c、d、i、j、n、s 和 t 代表什么?从这篇文章的赞成票和 cmets 的数量来看,我可以假设这段代码已被复制粘贴到各地的生产网站中......如果有一天它有错误,祝你好运!
  • “诗歌”?更像是默默无闻。这不是代码高尔夫;使用一点空白。正确的 var 名称也不会受到伤害。
【解决方案3】:

短而快速的解决方案(适用于任何地方!)

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

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

测试:

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"

演示: 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/


超扩展短解

在这个super extended version中你可以设置不同的分隔符类型:

/**
 * 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/

【讨论】:

  • 其实我更进一步:.replace(/(\d)(?=(\d{3})+(?:\.\d+)?$)/g, "$1,").
  • CoffeeScript 版本,带有 VisioN 和 kalisjoshua 正则表达式以及指定小数位的方式(因此您可以保留默认值 2 或指定 0 表示无小数):Number.prototype.toMoney = (decimal=2) -&gt; @toFixed(decimal).replace /(\d)(?=(\d{3})+(?:\.\d+)?$)/g, "$1,"
  • @Abbas 是的,将\. 替换为$(行尾),即this.toFixed(0).replace(/(\d)(?=(\d{3})+$)/g, "$1,")
  • @hanumant 这里的正则语法有点复杂,所以我建议你先阅读有关正则表达式的手册(例如MDN)。它背后的想法是用第一个匹配和逗号替换匹配的部分,即$1,。匹配是使用lookahead approach 完成的。您可以将表达式理解为“匹配一个数字,如果它后跟三个数字集(一个或多个)和一个点的序列”
  • @JuliendePrabère 请举一个不适合这种方法的长数字示例。
【解决方案4】:

查看 JavaScript Number 对象,看看它是否可以帮助您。

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

要同时使用它们,值的类型必须改回数字,因为它们都输出字符串。

例子:

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

编辑

直接使用 toLocaleString 即可,无需重铸为数字:

someNumber.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});

多个数字

如果您需要频繁地对数字进行类似的格式化,您可以创建一个特定的对象以供重复使用。喜欢德语(瑞士):

const money = new Intl.NumberFormat('de-CH',
  { style:'currency', currency: 'CHF' });
const percent = new Intl.NumberFormat('de-CH',
  { style:'percent', maximumFractionDigits: 1, signDisplay: "always"});

可以用作:

money.format(1234.50); // output CHF 1'234.50
percent.format(0.083);  // output +8.3%

非常漂亮。

【讨论】:

  • 谢谢!基于这个想法,我能够制作一个足够短和简单的! (和本地化)非常好。
  • 其实可以的。即对于美元:'$'+(value + 0.001).toLocaleString().slice(0,-1)
  • 看起来不错,但目前浏览器支持很少
  • 应该注意有一个旧版本的toLocaleString 使用系统区域设置,以及一个来自 ECMAScript Intl API 的新版本(不兼容)。 Explained here。这个答案似乎是针对旧版本的。
  • 不知道为什么投票率如此之高,但这并不能满足 OP 的要求。例如,10000 将变成 "10,000" 而不是 "10,000.00",这是货币格式化的理想行为。
【解决方案5】:

下面是 Patrick Desjardins (alias Daok) 代码,添加了一些 cmets 和一些小改动:

/*
decimal_sep: character used as decimal separator, 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 the 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. 我们按照How best to determine if an argument is not sent to the JavaScript function 中的建议使用typeof thousands_sep === 'undefined'

  4. 不需要(+n || 0),因为thisNumber 对象

JSFiddle

【讨论】:

  • 您可能希望使用 '10' 作为 parseInt 中的基数。否则,任何以“0”开头的数字都将使用八进制编号。
  • @sohtimsso1970:很抱歉回复晚了,但你能解释一下吗?我看不出哪里可以将数字解释为八进制。 parseInt 在数字的 INTEGER 部分的绝对值上调用。 INTEGER 部分不能以零开头,除非它只是一个零! parseInt(0) === 0 八进制或十进制。
  • 尝试,例如:parseInt("016") ... 返回 14,因为 parseInt 假定它是八进制编码的,当字符串以零开头时。
  • @Tracker1:我知道以0 开头的数字被parseInt 视为八进制。但是在这段代码中,parseInt 不可能接收016 作为输入(或任何其他八进制格式的值),因为传递给parseInt 的参数首先由Math.abs 函数处理。所以parseInt 无法接收以零开头的数字,除非它只是零或0.nn(其中nn 是小数)。但是00.nn 字符串都将被parseInt 转换成一个简单的零。
  • 这个函数不正确:> (2030).toMoney(0, '.', ' ');
【解决方案6】:

如果金额是一个数字,比如-123,那么

amount.toLocaleString('en-US', { style: 'currency', currency: 'USD' });

将产生字符串"-$123.00"

这是一个完整的工作example

【讨论】:

  • 这个答案对我来说几乎就在那里,但我需要将它四舍五入到最接近的一美分。这就是我使用的 amount.toLocaleString('en-GB', { style: 'currency', currency: 'GBP', maximumFractionDigits: 2 });
  • 在 Safari 中似乎不起作用。它只是将数字作为字符串返回,没有任何格式。
  • MDN Documentation 供任何想查看全套选项的人使用。
  • 哇,这是一个非常好的答案。应该是顶级的。
  • 如果由于某种原因您不想要美分,您可以使用以下命令更改小数精度:minimumFractionDigits: 0
【解决方案7】:

accounting.js 是一个用于数字、货币和货币格式的小型 JavaScript 库。

【讨论】:

  • ...记得传递货币符号,否则在 IE7 和 IE8 中会出错,IE9 都可以
  • 看起来 IE7/IE8 的 bug 已修复。
  • 这是一个很棒的库,能够传递货币符号也是一个好主意,因为所有货币详细信息都包含在单个函数调用/设置中
  • 我喜欢你可以做相反的事情——传递一个格式化的货币字符串并获取数值。
  • accounting.js 最近似乎没有维护。最近更改的一个分支是github.com/nashdot/accounting-js
【解决方案8】:

这是我见过的最好的 JavaScript 货币格式化程序:

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) : "");
};

它被重新格式化并从这里借用:How to format numbers as currency strings

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

这样称呼它(尽管请注意参数默认为 2、逗号和句点,因此如果您愿意,则无​​需提供任何参数):

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

【讨论】:

  • 注意全局符号,i,j
  • @hacklikecrack,所有变量都是本地的;他们在var 声明中。
  • 抱歉,是的,尽管您正在重新声明参数。缩进! ;)
  • 可怕地使用了变量名!
【解决方案9】:

这是另一个尝试,只是为了好玩:

function formatDollar(num) {
    var p = num.toFixed(2).split(".");
    return "$" + p[0].split("").reverse().reduce(function(acc, num, i, orig) {
        return num + (num != "-" && i && !(i % 3) ? "," : "") + acc;
    }, "") + "." + p[1];
}

还有一些测试:

formatDollar(45664544.23423) // "$45,664,544.23"
formatDollar(45) // "$45.00"
formatDollar(123) // "$123.00"
formatDollar(7824) // "$7,824.00"
formatDollar(1) // "$1.00"
formatDollar(-1345) // "$-1,345.00
formatDollar(-3) // "$-3.00"

【讨论】:

  • 诗歌。杰出的。你试过 reduceRight() developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… 应该消除 reverse() 吗?
  • @Steve - 你是对的,但你需要在回调中执行类似i = orig.length - i - 1 的操作。尽管如此,还是少了一次数组遍历。
  • 与兼容性无关:reduce 方法是在 Ecmascript 1.8 中引入的,在 Internet Explorer 8 及更低版本中不受支持。
  • 就像@Blaise 说的,这种方法在IE 8 或更低版本中不起作用。
  • 不适用于负数
【解决方案10】:

Works for all current browsers

使用toLocaleString 以语言敏感表示形式格式化货币(使用ISO 4217 货币代码)。

(2500).toLocaleString("en-GB", {style: "currency", currency: "GBP", minimumFractionDigits: 2})

示例南非兰德代码 sn-ps for avenmore

console.log((2500).toLocaleString("en-ZA", {style: "currency", currency: "ZAR", minimumFractionDigits: 2}))
// -> R 2 500,00
console.log((2500).toLocaleString("en-GB", {style: "currency", currency: "ZAR", minimumFractionDigits: 2}))
// -> ZAR 2,500.00

【讨论】:

  • 因为 'locales' 和 'options' 参数仅被极少数浏览器支持,例如 Chrome 24、IE11 和 Opera 15。Firefox、Safari 和其他旧版本仍然不支持它。
  • 同意,所有浏览器(目前)还没有完全支持它,但它仍然是一个解决方案。 (并且可以说是最有效的解决方案,因为它与不受支持的浏览器向前兼容,并且它是 Javascript api 的文档化功能。)
  • 我喜欢这个并且很高兴它适用于印度数字分组。
  • 自 2017 年起已完全支持,应该是唯一正确的答案
  • 最新最好的:) FF69、Chrome76等。这里用的不是“R 2 500,00”,应该是“R 2,500.00”,和en-GB一样。
【解决方案11】:

我想你想要:

f.nettotal.value = "$" + showValue.toFixed(2);

【讨论】:

  • @crush 这行得通,但它不再将计算带入税务领域?
  • 添加$符号后,它不再是数字,而是字符串。
  • 此选项不会在千位之间放置逗号。 :-(
【解决方案12】:

好的,根据你说的,我正在使用这个:

var DecimalSeparator = Number("1.2").toLocaleString().substr(1,1);

var AmountWithCommas = Amount.toLocaleString();
var arParts = String(AmountWithCommas).split(DecimalSeparator);
var intPart = arParts[0];
var decPart = (arParts.length > 1 ? arParts[1] : '');
decPart = (decPart + '00').substr(0,2);

return '£ ' + intPart + DecimalSeparator + decPart;

我对改进建议持开放态度(我不想仅仅为了这样做而包含YUI :-))

我已经知道我应该检测“。”而不是仅仅将其用作小数分隔符...

【讨论】:

  • 请注意,您的版本没有正确四舍五入到两位小数。例如,3.706 将被格式化为“3.70 英镑”,而不是应有的“3.71 英镑”。
  • 是的,在我的特定情况下没关系,因为我正在使用的金额最多已经有 2 位数字我需要固定为 2 位小数的原因是没有小数位或只有 1 位的金额.
【解决方案13】:

Numeral.js - @adamwdraper 用于轻松数字格式化的 JavaScript 库

numeral(23456.789).format('$0,0.00'); // = "$23,456.79"

【讨论】:

【解决方案14】:

我使用库Globalize(来自微软):

这是一个很好的项目,可以本地化数字、货币和日期,并根据用户的区域设置自动以正确的方式格式化它们! ...尽管它应该是一个 jQuery 扩展,但它目前是一个 100% 独立的库。建议大家试试看! :)

【讨论】:

  • 哇,为什么没有更多的支持?用于各种格式的大型标准化库。具有正确全球化的行业标准格式参数。很好的答案!
  • 它仍然被认为是 alpha 阶段,所以谨慎使用,但很好的发现。
  • 不再是 alpha(或 beta)。在我们等待 Safari 满足新标准并等待 IE
【解决方案15】:

以下内容简洁易懂,不依赖任何过于复杂的正则表达式。

function moneyFormat(price, sign = '$') {
  const pieces = parseFloat(price).toFixed(2).split('')
  let ii = pieces.length - 3
  while ((ii-=3) > 0) {
    pieces.splice(ii, 0, ',')
  }
  return sign + pieces.join('')
}

console.log(
  moneyFormat(100),
  moneyFormat(1000),
  moneyFormat(10000.00),
  moneyFormat(1000000000000000000)
)

这是一个在最终输出中包含更多选项的版本,以允许以不同的地区格式格式化不同的货币。

// higher order function that takes options then a price and will return the formatted price
const makeMoneyFormatter = ({
  sign = '$',
  delimiter = ',',
  decimal = '.',
  append = false,
  precision = 2,
  round = true,
  custom
} = {}) => value => {

  const e = [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000]

  value = round
    ? (Math.round(value * e[precision]) / e[precision])
    : parseFloat(value)

  const pieces = value
    .toFixed(precision)
    .replace('.', decimal)
    .split('')

  let ii = pieces.length - (precision ? precision + 1 : 0)

  while ((ii-=3) > 0) {
    pieces.splice(ii, 0, delimiter)
  }

  if (typeof custom === 'function') {
    return custom({
      sign,
      float: value,
      value: pieces.join('')
    })
  }

  return append
    ? pieces.join('') + sign
    : sign + pieces.join('')
}

// create currency converters with the correct formatting options
const formatDollar = makeMoneyFormatter()
const formatPound = makeMoneyFormatter({
  sign: '£',
  precision: 0
})
const formatEuro = makeMoneyFormatter({
  sign: '€',
  delimiter: '.',
  decimal: ',',
  append: true
})

const customFormat = makeMoneyFormatter({
  round: false,
  custom: ({ value, float, sign }) => `SALE:$${value}USD`
})

console.log(
  formatPound(1000),
  formatDollar(10000.0066),
  formatEuro(100000.001),
  customFormat(999999.555)
)

【讨论】:

  • 很棒的代码 sn-p,谢谢。但是,要小心,因为它在 IE 上不起作用,因为不支持默认参数,并且
  • 不用担心@CharlieDalsass。我建议使用 babel 将其编译为 ES5 以用于生产代码。
  • 但是欧元怎么办? 1.000,00 欧元?
  • @YumYumYum 我添加了一个完整的示例,其中包含更多格式选项以提供更大的灵活性。
【解决方案16】:

javascript-number-formatter(以前的at Google Code

  • 简短、快速、灵活且独立。
  • 接受标准数字格式,如#,##0.00 或否定-000.####
  • 接受任何国家/地区格式,例如# ##0,00#,###.###'###.## 或任何类型的非编号符号。
  • 接受任意数量的数字分组。 #,##,#0.000#,###0.## 均有效。
  • 接受任何冗余/万无一失的格式。 ##,###,##.#0#,#00#.###0# 都可以。
  • 自动数字舍入。
  • 简单的界面,只需像这样提供掩码和值:format( "0.0000", 3.141592)
  • 在掩码中包含前缀和后缀

(摘自自述文件)

【讨论】:

    【解决方案17】:

    使用正则表达式的更短的方法(用于插入空格、逗号或点):

        Number.prototype.toCurrencyString = function(){
            return this.toFixed(2).replace(/(\d)(?=(\d{3})+\b)/g, '$1 ');
        }
    
        n = 12345678.9;
        alert(n.toCurrencyString());
    

    【讨论】:

    • 这太棒了!应该是最好的答案,!
    【解决方案18】:

    +1 到Jonathan M for providing the original method。由于这是一个明确的货币格式化程序,我继续在输出中添加了货币符号(默认为“$”),并添加了一个默认逗号作为千位分隔符。如果您实际上不需要货币符号(或千位分隔符),只需使用“”(空字符串)作为参数。

    Number.prototype.formatMoney = function(decPlaces, thouSeparator, decSeparator, currencySymbol) {
        // check the args and supply defaults:
        decPlaces = isNaN(decPlaces = Math.abs(decPlaces)) ? 2 : decPlaces;
        decSeparator = decSeparator == undefined ? "." : decSeparator;
        thouSeparator = thouSeparator == undefined ? "," : thouSeparator;
        currencySymbol = currencySymbol == undefined ? "$" : currencySymbol;
    
        var n = this,
            sign = n < 0 ? "-" : "",
            i = parseInt(n = Math.abs(+n || 0).toFixed(decPlaces)) + "",
            j = (j = i.length) > 3 ? j % 3 : 0;
    
        return sign + currencySymbol + (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) : "");
    };
    

    【讨论】:

    • 第一个变量有点奇怪,因为这些变量已经在函数声明中声明了。除此之外,谢谢!
    • 你是对的。这是我从 Jonathan M 的原文中引入的一个错误,它们都被链接为单个 var 表达式。这些应该是简单的任务。修复。
    • 就此而言,我认为这可能是过早的优化,应该重构以提高可读性。但我的目标是扩充 OP 的代码,而不是从根本上改变它。
    • 还不错——+n || 0 是唯一有点奇怪的东西(无论如何对我来说)。
    • this 是一个非常有用的变量名。将其转换为 n 以便您可以在定义时保存 3 个字符在 RAM 和带宽以 KB 计算的时代可能是必要的,但在缩小器将在它之前处理所有这些的时代仅仅是混淆打击生产。其他巧妙的微优化至少值得商榷。
    【解决方案19】:

    主要部分是插入千位分隔符,可以这样完成:

    <script type="text/javascript">
      function ins1000Sep(val) {
        val = val.split(".");
        val[0] = val[0].split("").reverse().join("");
        val[0] = val[0].replace(/(\d{3})/g, "$1,");
        val[0] = val[0].split("").reverse().join("");
        val[0] = val[0].indexOf(",") == 0 ? val[0].substring(1) : val[0];
        return val.join(".");
      }
    
      function rem1000Sep(val) {
        return val.replace(/,/g, "");
      }
    
      function formatNum(val) {
        val = Math.round(val*100)/100;
        val = ("" + val).indexOf(".") > -1 ? val + "00" : val + ".00";
        var dec = val.indexOf(".");
        return dec == val.length-3 || dec == 0 ? val : val.substring(0, dec+3);
      }
    </script>
    
    <button onclick="alert(ins1000Sep(formatNum(12313231)));">
    

    【讨论】:

    • 我在向 ins1000Sep() 输入负值时得到错误的数字输出。
    【解决方案20】:

    PHP 函数“number_format”有一个 JavaScript 端口。

    我觉得它非常有用,因为它易于使用并且 PHP 开发人员可以识别它。

    function number_format (number, decimals, dec_point, thousands_sep) {
        var n = number, prec = decimals;
    
        var toFixedFix = function (n,prec) {
            var k = Math.pow(10,prec);
            return (Math.round(n*k)/k).toString();
        };
    
        n = !isFinite(+n) ? 0 : +n;
        prec = !isFinite(+prec) ? 0 : Math.abs(prec);
        var sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep;
        var dec = (typeof dec_point === 'undefined') ? '.' : dec_point;
    
        var s = (prec > 0) ? toFixedFix(n, prec) : toFixedFix(Math.round(n), prec);
        // Fix for Internet Explorer parseFloat(0.55).toFixed(0) = 0;
    
        var abs = toFixedFix(Math.abs(n), prec);
        var _, i;
    
        if (abs >= 1000) {
            _ = abs.split(/\D/);
            i = _[0].length % 3 || 3;
    
            _[0] = s.slice(0,i + (n < 0)) +
                   _[0].slice(i).replace(/(\d{3})/g, sep+'$1');
            s = _.join(dec);
        } else {
            s = s.replace('.', dec);
        }
    
        var decPos = s.indexOf(dec);
        if (prec >= 1 && decPos !== -1 && (s.length-decPos-1) < prec) {
            s += new Array(prec-(s.length-decPos-1)).join(0)+'0';
        }
        else if (prec >= 1 && decPos === -1) {
            s += dec+new Array(prec).join(0)+'0';
        }
        return s;
    }
    

    (来自the original 的评论块,包括在下面的示例和到期的信用)

    // Formats a number with grouped thousands
    //
    // version: 906.1806
    // discuss at: http://phpjs.org/functions/number_format
    // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +     bugfix by: Michael White (http://getsprink.com)
    // +     bugfix by: Benjamin Lupton
    // +     bugfix by: Allan Jensen (http://www.winternet.no)
    // +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +     bugfix by: Howard Yeend
    // +    revised by: Luke Smith (http://lucassmith.name)
    // +     bugfix by: Diogo Resende
    // +     bugfix by: Rival
    // +     input by: Kheang Hok Chin (http://www.distantia.ca/)
    // +     improved by: davook
    // +     improved by: Brett Zamir (http://brett-zamir.me)
    // +     input by: Jay Klehr
    // +     improved by: Brett Zamir (http://brett-zamir.me)
    // +     input by: Amir Habibi (http://www.residence-mixte.com/)
    // +     bugfix by: Brett Zamir (http://brett-zamir.me)
    // *     example 1: number_format(1234.56);
    // *     returns 1: '1,235'
    // *     example 2: number_format(1234.56, 2, ',', ' ');
    // *     returns 2: '1 234,56'
    // *     example 3: number_format(1234.5678, 2, '.', '');
    // *     returns 3: '1234.57'
    // *     example 4: number_format(67, 2, ',', '.');
    // *     returns 4: '67,00'
    // *     example 5: number_format(1000);
    // *     returns 5: '1,000'
    // *     example 6: number_format(67.311, 2);
    // *     returns 6: '67.31'
    // *     example 7: number_format(1000.55, 1);
    // *     returns 7: '1,000.6'
    // *     example 8: number_format(67000, 5, ',', '.');
    // *     returns 8: '67.000,00000'
    // *     example 9: number_format(0.9, 0);
    // *     returns 9: '1'
    // *     example 10: number_format('1.20', 2);
    // *     returns 10: '1.20'
    // *     example 11: number_format('1.20', 4);
    // *     returns 11: '1.2000'
    // *     example 12: number_format('1.2000', 3);
    // *     returns 12: '1.200'
    

    【讨论】:

    • 这只是一个正确的函数: > number_format(2030, 0, '.', ' ')
    【解决方案21】:

    Patrick Desjardins' 的答案看起来不错,但我更喜欢我的 JavaScript 代码简单。这是我刚刚编写的一个函数,用于输入一个数字并以货币格式(减去美元符号)返回它:

    // Format numbers to two decimals with commas
    function formatDollar(num) {
        var p = num.toFixed(2).split(".");
        var chars = p[0].split("").reverse();
        var newstr = '';
        var count = 0;
        for (x in chars) {
            count++;
            if(count%3 == 1 && count != 1) {
                newstr = chars[x] + ',' + newstr;
            } else {
                newstr = chars[x] + newstr;
            }
        }
        return newstr + "." + p[1];
    }
    

    【讨论】:

    • 我需要一些东西可以在浏览器和旧版本的 Node.js 中工作。这非常有效。谢谢
    【解决方案22】:

    在 JavaScript 中有一个内置函数 toFixed

    var num = new Number(349);
    document.write("$" + num.toFixed(2));
    

    【讨论】:

    • 这个答案看起来是多余的。粉碎的答案已经提到toFixed()
    • toFixed()Number 对象的函数,如果它是String,则不会在var num 上工作,所以额外的上下文对我有帮助。
    【解决方案23】:
    function CurrencyFormatted(amount)
    {
        var i = parseFloat(amount);
        if(isNaN(i)) { i = 0.00; }
        var minus = '';
        if(i < 0) { minus = '-'; }
        i = Math.abs(i);
        i = parseInt((i + .005) * 100);
        i = i / 100;
        s = new String(i);
        if(s.indexOf('.') < 0) { s += '.00'; }
        if(s.indexOf('.') == (s.length - 2)) { s += '0'; }
        s = minus + s;
        return s;
    }
    

    来自WillMaster

    【讨论】:

    • 小而简单。谢谢。
    • 简单,但 1,000 没有逗号
    【解决方案24】:

    我建议 Google Visualization API 的 NumberFormat 类。

    你可以这样做:

    var formatter = new google.visualization.NumberFormat({
        prefix: '$',
        pattern: '#,###,###.##'
    });
    
    formatter.formatValue(1000000); // $ 1,000,000
    

    【讨论】:

      【解决方案25】:

      通常,有多种方法可以做同样的事情,但我会避免使用Number.prototype.toLocaleString,因为它可以根据用户设置返回不同的值。

      我也不建议扩展Number.prototype - 扩展原生对象原型是一种不好的做法,因为它可能会导致与其他人的代码(例如库/框架/插件)发生冲突,并且可能与未来的 JavaScript 实现不兼容/版本。

      我相信正则表达式是解决问题的最佳方法,这是我的实现:

      /**
       * Converts number into currency format
       * @param {number} number    Number that should be converted.
       * @param {string} [decimalSeparator]    Decimal separator, defaults to '.'.
       * @param {string} [thousandsSeparator]    Thousands separator, defaults to ','.
       * @param {int} [nDecimalDigits]    Number of decimal digits, defaults to `2`.
       * @return {string} Formatted string (e.g. numberToCurrency(12345.67) returns '12,345.67')
       */
      function numberToCurrency(number, decimalSeparator, thousandsSeparator, nDecimalDigits){
          //default values
          decimalSeparator = decimalSeparator || '.';
          thousandsSeparator = thousandsSeparator || ',';
          nDecimalDigits = nDecimalDigits == null? 2 : nDecimalDigits;
      
          var fixed = number.toFixed(nDecimalDigits), //limit/add decimal digits
              parts = new RegExp('^(-?\\d{1,3})((?:\\d{3})+)(\\.(\\d{'+ nDecimalDigits +'}))?$').exec( fixed ); //separate begin [$1], middle [$2] and decimal digits [$4]
      
          if(parts){ //number >= 1000 || number <= -1000
              return parts[1] + parts[2].replace(/\d{3}/g, thousandsSeparator + '$&') + (parts[4] ? decimalSeparator + parts[4] : '');
          }else{
              return fixed.replace('.', decimalSeparator);
          }
      }
      

      【讨论】:

      • toLocaleString 的关键在于它确实会根据用户的设置进行调整。
      【解决方案26】:

      这可能有点晚了,但这是我刚刚为一位同事设计的一种方法,可以为所有数字添加一个区域设置感知 .toCurrencyString() 函数。内部化仅用于数字分组,不是货币符号 - 如果您输出美元,请使用提供的 "$",因为在日本或中国,$123 4567 与 @ 的美元数相同987654324@ 在美国。如果您输出欧元等,则将货币符号从"$"更改。

      在您的 HTML

      部分的任何地方或任何需要的地方声明它,就在您需要使用它之前:
        Number.prototype.toCurrencyString = function(prefix, suffix) {
          if (typeof prefix === 'undefined') { prefix = '$'; }
          if (typeof suffix === 'undefined') { suffix = ''; }
          var _localeBug = new RegExp((1).toLocaleString().replace(/^1/, '').replace(/\./, '\\.') + "$");
          return prefix + (~~this).toLocaleString().replace(_localeBug, '') + (this % 1).toFixed(2).toLocaleString().replace(/^[+-]?0+/,'') + suffix;
        }
      

      那么你就完成了!在需要将数字输出为货币的任何地方使用(number).toCurrencyString()

      var MyNumber = 123456789.125;
      alert(MyNumber.toCurrencyString()); // alerts "$123,456,789.13"
      MyNumber = -123.567;
      alert(MyNumber.toCurrencyString()); // alerts "$-123.57"
      

      【讨论】:

        【解决方案27】:

        只使用原生javascript Inlt

        你只需使用选项来格式化它的值

        const number = 1233445.5678
        console.log(new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(number));

        mozilla documentation link

        【讨论】:

        【解决方案28】:

        请尝试以下代码

        "250000".replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
        

        回答:250,000

        【讨论】:

          【解决方案29】:

          Number(value)
                  .toFixed(2)
                  .replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,")

          【讨论】:

          • 解释一下。例如,9 年(当时)过去 50 多个答案中的任何一个都没有涵盖它,怎么可能如此简单?
          【解决方案30】:

          这里有一些解决方案,都通过了测试套件。包括测试套件和基准。如果你想复制粘贴来测试,试试this gist

          方法 0(正则表达式)

          它基于VisioN's answer,但如果没有小数点,它会修复。

          if (typeof Number.prototype.format === 'undefined') {
              Number.prototype.format = function (precision) {
                  if (!isFinite(this)) {
                      return this.toString();
                  }
          
                  var a = this.toFixed(precision).split('.');
                  a[0] = a[0].replace(/\d(?=(\d{3})+$)/g, '$&,');
                  return a.join('.');
              }
          }
          

          方法一

          if (typeof Number.prototype.format === 'undefined') {
              Number.prototype.format = function (precision) {
                  if (!isFinite(this)) {
                      return this.toString();
                  }
          
                  var a = this.toFixed(precision).split('.'),
                      // Skip the '-' sign
                      head = Number(this < 0);
          
                  // Skip the digits that's before the first thousands separator
                  head += (a[0].length - head) % 3 || 3;
          
                  a[0] = a[0].slice(0, head) + a[0].slice(head).replace(/\d{3}/g, ',$&');
                  return a.join('.');
              };
          }
          

          方法2(拆分为数组)

          if (typeof Number.prototype.format === 'undefined') {
              Number.prototype.format = function (precision) {
                  if (!isFinite(this)) {
                      return this.toString();
                  }
          
                  var a = this.toFixed(precision).split('.');
          
                  a[0] = a[0]
                      .split('').reverse().join('')
                      .replace(/\d{3}(?=\d)/g, '$&,')
                      .split('').reverse().join('');
          
                  return a.join('.');
              };
          }
          

          方法3(循环)

          if (typeof Number.prototype.format === 'undefined') {
              Number.prototype.format = function (precision) {
                  if (!isFinite(this)) {
                      return this.toString();
                  }
          
                  var a = this.toFixed(precision).split('');
                  a.push('.');
          
                  var i = a.indexOf('.') - 3;
                  while (i > 0 && a[i-1] !== '-') {
                      a.splice(i, 0, ',');
                      i -= 3;
                  }
          
                  a.pop();
                  return a.join('');
              };
          }
          

          使用示例

          console.log('======== Demo ========')
          console.log(
              (1234567).format(0),
              (1234.56).format(2),
              (-1234.56).format(0)
          );
          var n = 0;
          for (var i=1; i<20; i++) {
              n = (n * 10) + (i % 10)/100;
              console.log(n.format(2), (-n).format(2));
          }
          

          分隔符

          如果我们想要自定义千位分隔符或小数分隔符,请使用replace()

          123456.78.format(2).replace(',', ' ').replace('.', ' ');
          

          测试套件

          function assertEqual(a, b) {
              if (a !== b) {
                  throw a + ' !== ' + b;
              }
          }
          
          function test(format_function) {
              console.log(format_function);
              assertEqual('NaN', format_function.call(NaN, 0))
              assertEqual('Infinity', format_function.call(Infinity, 0))
              assertEqual('-Infinity', format_function.call(-Infinity, 0))
          
              assertEqual('0', format_function.call(0, 0))
              assertEqual('0.00', format_function.call(0, 2))
              assertEqual('1', format_function.call(1, 0))
              assertEqual('-1', format_function.call(-1, 0))
          
              // Decimal padding
              assertEqual('1.00', format_function.call(1, 2))
              assertEqual('-1.00', format_function.call(-1, 2))
          
              // Decimal rounding
              assertEqual('0.12', format_function.call(0.123456, 2))
              assertEqual('0.1235', format_function.call(0.123456, 4))
              assertEqual('-0.12', format_function.call(-0.123456, 2))
              assertEqual('-0.1235', format_function.call(-0.123456, 4))
          
              // Thousands separator
              assertEqual('1,234', format_function.call(1234.123456, 0))
              assertEqual('12,345', format_function.call(12345.123456, 0))
              assertEqual('123,456', format_function.call(123456.123456, 0))
              assertEqual('1,234,567', format_function.call(1234567.123456, 0))
              assertEqual('12,345,678', format_function.call(12345678.123456, 0))
              assertEqual('123,456,789', format_function.call(123456789.123456, 0))
              assertEqual('-1,234', format_function.call(-1234.123456, 0))
              assertEqual('-12,345', format_function.call(-12345.123456, 0))
              assertEqual('-123,456', format_function.call(-123456.123456, 0))
              assertEqual('-1,234,567', format_function.call(-1234567.123456, 0))
              assertEqual('-12,345,678', format_function.call(-12345678.123456, 0))
              assertEqual('-123,456,789', format_function.call(-123456789.123456, 0))
          
              // Thousands separator and decimal
              assertEqual('1,234.12', format_function.call(1234.123456, 2))
              assertEqual('12,345.12', format_function.call(12345.123456, 2))
              assertEqual('123,456.12', format_function.call(123456.123456, 2))
              assertEqual('1,234,567.12', format_function.call(1234567.123456, 2))
              assertEqual('12,345,678.12', format_function.call(12345678.123456, 2))
              assertEqual('123,456,789.12', format_function.call(123456789.123456, 2))
              assertEqual('-1,234.12', format_function.call(-1234.123456, 2))
              assertEqual('-12,345.12', format_function.call(-12345.123456, 2))
              assertEqual('-123,456.12', format_function.call(-123456.123456, 2))
              assertEqual('-1,234,567.12', format_function.call(-1234567.123456, 2))
              assertEqual('-12,345,678.12', format_function.call(-12345678.123456, 2))
              assertEqual('-123,456,789.12', format_function.call(-123456789.123456, 2))
          }
          
          console.log('======== Testing ========');
          test(Number.prototype.format);
          test(Number.prototype.format1);
          test(Number.prototype.format2);
          test(Number.prototype.format3);
          

          基准测试

          function benchmark(f) {
              var start = new Date().getTime();
              f();
              return new Date().getTime() - start;
          }
          
          function benchmark_format(f) {
              console.log(f);
              time = benchmark(function () {
                  for (var i = 0; i < 100000; i++) {
                      f.call(123456789, 0);
                      f.call(123456789, 2);
                  }
              });
              console.log(time.format(0) + 'ms');
          }
          
          // If not using async, the browser will stop responding while running.
          // This will create a new thread to benchmark
          async = [];
          function next() {
              setTimeout(function () {
                  f = async.shift();
                  f && f();
                  next();
              }, 10);
          }
          
          console.log('======== Benchmark ========');
          async.push(function () { benchmark_format(Number.prototype.format); });
          next();
          

          【讨论】:

          • 从您的方法改进 2. 从 var a = this.toFixed(precision).split('.') 更改为 var multiplier = Math.pow( 10, precision + 1 ), wholeNumber = Math.floor(这个*乘数); var a = Math.round( WholeNumber / 10 ) * 10 / 乘数; if (String(a).indexOf('.')
          • console.log(parseFloat('4.835').toFixed(2)); > 4.83 控制台.log(parseFloat('54.835').toFixed(2)); > 54.84 控制台.log(parseFloat('454.835').toFixed(2)); > 454.83 控制台.log(parseFloat('8454.835').toFixed(2)); > 8454.83 所有这些值的小数点应该是 .84 而不是 .83
          猜你喜欢
          • 1970-01-01
          • 2011-05-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-14
          相关资源
          最近更新 更多