我想用JavaScript格式化价格。我想要一个函数,它将浮点作为参数,并返回如下格式的字符串:

"$ 2,500.00"

我该怎么做?


当前回答

我喜欢VisionN的最短答案,除非我需要修改一个没有小数点的数字(123美元而不是123.00美元)。它不起作用,所以我需要破解JavaScript正则表达式的神秘语法,而不是快速复制/粘贴。

这是最初的解决方案

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

我会再延长一点:

var re = /\d(?=(\d{3})+\.)/g;
var subst = '$&,';
n.toFixed(2).replace(re, subst);

此处的re部分(字符串替换中的搜索部分)表示

查找所有数字(\d)后跟(?=…)(展望)一个或多个组(…)+正好三位数(\d{3})以点(\.)结尾对所有事件执行此操作(g)

这里的子部分是指:

每次有匹配项时,将其替换为自身($&),后跟逗号。

当我们使用string.replace时,字符串中的所有其他文本保持不变,只有找到的数字(后面跟着3、6、9等其他数字的数字)才会得到一个额外的逗号。

因此,在数字1234567.89中,数字1和4满足条件(1234567.89),并被替换为“1”和“4”,从而得到1234567.89。

如果我们根本不需要美元金额的小数点(即123美元而不是123.00美元),我们可以这样更改正则表达式:

var re2 = /\d(?=(\d{3})+$)/g;

它依赖于行尾($)而不是点(\.),最后的表达式将是(请注意Fixed(0)):

n.toFixed(0).replace(/\d(?=(\d{3})+$)/g, '$&,');

此表达式将给出

1234567.89 -> 1,234,567

此外,您也可以选择单词边界(\b),而不是上面正则表达式中的行尾($)。

如果我误解了正则表达式处理的任何部分,我会提前道歉。

其他回答

javascript数字格式化程序(以前在Google Code)

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

(摘自自述)

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

方法0(RegExp)

它是基于VisioN的答案,但如果没有小数点,它会修复。

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('.');
    }
}

方法1

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();

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat示例:使用区域设置

此示例显示了本地化数字格式的一些变体。为了获得应用程序用户界面中使用的语言的格式,请确保使用locales参数指定该语言(可能还有一些回退语言):

变量编号=12346.789;//德语使用逗号作为小数分隔符,句点表示千console.log(新Intl.NumberFormat('de-de').format(数字));//→123.456,789//大多数阿拉伯语国家的阿拉伯语使用真正的阿拉伯数字console.log(新Intl.NumberFormat('ar-EG').format(数字));//→١٢٣٤٥٦٫٧٨٩//印度使用数千/十万/千个分隔符console.log(新Intl.NumberFormat('en-IN').format(数字));

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

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

次要变化包括:

移动了一点Math.abs(小数),只有当不是NaN时才能执行。decimal_sep不能再是空字符串(必须使用某种十进制分隔符)我们使用typeof thousand_sep===“undefined”,如How best to determine if a argument is not send to JavaScript function中所建议的不需要(+n||0),因为这是Number对象

JSFiddle公司

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

函数moneyFormat(价格,符号=“$”){const pieces=parseFloat(price).toFixed(2).split(“”)设ii=件长度-3而((ii-=3)>0){拼接件(ii,0,',')}return符号+pieces.join(“”)}控制台日志(money格式(100),money格式(1000),货币格式(10000.00),货币格式(1000000000000000000))

这是一个在最终输出中具有更多选项的版本,允许以不同的位置格式格式化不同的货币。

//高阶函数,接受期权,然后返回价格,并返回格式化的价格常量makeMoneyFormatter=({符号=“$”,分隔符=',',十进制='.',append=false,精度=2,round=真,风俗}={})=>值=>{常量=[1,10,100,1000,10000,100000,1000000,10000000]值=圆形? (数学舍入(值*e[precision])/e[precisity]):parseFloat(值)常量件=值.to固定(精度).replace('.',十进制).split(“”)设ii=工件长度-(精度?精度+1:0)而((ii-=3)>0){件.拼接(ii,0,分隔符)}if(自定义类型==“函数”){返回自定义项({签名float:值,值:pieces.join(“”)})}返回追加? pieces.join(“”)+符号:sign+pieces.join(“”)}//使用正确的格式选项创建货币转换器const formatDollar=makeMoneyFormatter()const formatPound=makeMoneyFormatter({符号:“£”,精度:0})const formatEuro=makeMoneyFormatter({符号:“€”,分隔符:“.”,十进制:',',附加:真})const customFormat=makeMoneyFormatter({圆形:假,custom:({value,float,sign})=>`SALE:$${value}USD`})控制台日志(格式磅(1000),格式美元(10000.0066),格式欧元(1000000.00),自定义格式(999999.555))