37

我想知道是否可以在 Javascript 模板字符串中格式化数字,例如:

var n = 5.1234;
console.log(`This is a number: $.2d{n}`);
// -> 5.12

或者可能

var n = 5.1234;
console.log(`This is a number: ${n.toString('.2d')}`);
// -> 5.12

该语法显然不起作用,它只是我正在寻找的事物类型的说明。

我知道像sprintffrom这样的工具underscore.string,但这似乎是 JS 应该能够开箱即用的东西,特别是考虑到模板字符串的力量。

编辑

如上所述,我已经知道 3rd 方工具(例如 sprintf)和自定义函数来执行此操作。类似的问题(例如JavaScript 等价于 printf/String.Format)根本没有提到模板字符串,可能是因为在 ES6 模板字符串出现之前就已经问过了。我的问题是 ES6 特有的,并且与实现无关。如果是这种情况,我很高兴接受“不,这是不可能的”的回答,但是最好的是有关提供此功能的新 ES6 功能的信息,或者对此类功能是否存在的一些见解方法。

4

7 回答 7

31

不,ES6 没有引入任何新的数字格式化功能,您将不得不使用现有的.toExponential(fractionDigits), .toFixed(fractionDigits), .toPrecision(precision), .toString([radix])and toLocaleString(…)(不过,它已更新为可选地支持ECMA-402 标准)。
模板字符串与数字格式无关,它们只是对函数调用(如果已标记)或字符串连接(默认)脱糖。

如果这些Number方法对您来说还不够,您将不得不自己动手。如果您愿意,您当然可以将格式化函数编写为模板字符串标记。

于 2015-07-31T14:07:29.777 回答
7

您应该能够使用数字的 toFixed() 方法:

var num = 5.1234;
var n = num.toFixed(2); 
于 2015-07-31T12:50:50.020 回答
1

这是上面 Filip Allberg 解决方案的完整 ES6 版本,使用 ES6“rest”参数。唯一缺少的是能够改变精度。这可以通过制作工厂功能来完成。留给读者作为练习。

function d2(strs, ...args) {
    var result = strs[0];
    for (var i = 0; i < args.length; ++i) {
        var n = args[i];
        if (Number(n) == n) {
            result += Number(args[i]).toFixed(2);
        } else {
            result += args[i];
        }
        result += strs[i+1];
    }
    return result;
}

f=1.2345678;
s="a string";
console.log(d2`template: ${f} ${f*100} and ${s} (literal:${9.0001})`);

于 2018-12-07T16:49:49.843 回答
1

如果你想使用 ES6 标签函数,下面是这样的标签函数的外观,

function d2(pieces) {
    var result = pieces[0];
    var substitutions = [].slice.call(arguments, 1);

    for (var i = 0; i < substitutions.length; ++i) {
        var n = substitutions[i];

        if (Number(n) == n) {
            result += Number(substitutions[i]).toFixed(2);
        } else {
            result += substitutions[i];
        }

        result += pieces[i + 1];
    }

    return result;
}

然后可以将其应用于模板字符串,

d2`${some_float} (you can interpolate as many floats as you want) of ${some_string}`;

这将格式化浮点数并单独保留字符串。

于 2018-08-03T21:25:21.750 回答
1

基于ES6 标记模板(归功于https://stackoverflow.com/a/51680250/711085),这将模拟其他语言中的典型模板字符串语法(这大致基于 python f-strings;我避免调用它f以防万一名称重叠):

演示:

> F`${(Math.sqrt(2))**2}{.0f}`  // normally 2.0000000000000004
"2"

> F`${1/3}{%} ~ ${1/3}{.2%} ~ ${1/3}{d} ~ ${1/3}{.2f} ~ ${1/3}"
"33% ~ 33.33% ~ 0 ~ 0.33 ~ 0.3333333333333333"

> F`${[1/3,1/3]}{.2f} ~ ${{a:1/3, b:1/3}}{.2f} ~ ${"someStr"}`
"[0.33,0.33] ~ {\"a\":\"0.33\",\"b\":\"0.33\"} ~ someStr

相当简单的代码使用:

var FORMATTER = function(obj,fmt) {
    /* implements things using (Number).toFixed:
       ${1/3}{.2f} -> 0.33
       ${1/3}{.0f} -> 1
       ${1/3}{%} -> 33%
       ${1/3}{.3%} -> 33.333%
       ${1/3}{d} -> 0
       ${{a:1/3,b:1/3}}{.2f} -> {"a":0.33, "b":0.33}
       ${{a:1/3,b:1/3}}{*:'.2f',b:'%'} -> {"a":0.33, "b":'33%'}  //TODO not implemented
       ${[1/3,1/3]}{.2f} -> [0.33, 0.33]
       ${someObj} -> if the object/class defines a method [Symbol.FTemplate](){...}, 
                     it will be evaluated; alternatively if a method [Symbol.FTemplateKey](key){...} 
                     that can be evaluated to a fmt string; alternatively in the future 
                     once decorators exist, metadata may be appended to object properties to derive 
                     formats //TODO not implemented
    */
    try {
        let fracDigits=0,percent;
        if (fmt===undefined) {
            if (typeof obj === 'string')
                return obj;
            else
                return JSON.stringify(obj);
        } else if (obj instanceof Array)
            return '['+obj.map(x=> FORMATTER(x,fmt))+']'
        else if (typeof obj==='object' && obj!==null /*&&!Array.isArray(obj)*/)
            return JSON.stringify(Object.fromEntries(Object.entries(obj).map(([k,v])=> [k,FORMATTER(v,fmt)])));
        else if (matches = fmt.match(/^\.(\d+)f$/))
            [_,fracDigits] = matches;
        else if (matches = fmt.match(/^(?:\.(\d+))?(%)$/))
            [_,fracDigits,percent] = matches;
        else if (matches = fmt.match(/^d$/))
            fracDigits = 0;
        else
            throw 'format not recognized';
        
        if (obj===null)
            return 'null';
        if (obj===undefined) {
            // one might extend the above syntax to 
            // allow for example for .3f? -> "undefined"|"0.123"
            return 'undefined';
        }
        
        if (percent)
            obj *= 100;
        
        fracDigits = parseFloat(fracDigits);
        return obj.toFixed(fracDigits) + (percent? '%':'');
    } catch(err) {
        throw `error executing F\`$\{${someObj}\}{${fmt}}\` specification: ${err}`
    }
}

function F(strs, ...args) {
    /* usage: F`Demo: 1+1.5 = ${1+1.5}{.2f}` 
          --> "Demo: 1+1.5 = 2.50" 
    */
    let R = strs[0];
    args.forEach((arg,i)=> {
        let [_,fmt,str] = strs[i+1].match(/(?:\{(.*)(?<!\\)\})?(.*)/);
        R += FORMATTER(arg,fmt) + str;
    });
    return R;
}

旁注:代码的核心如下。繁重的工作由格式化程序完成。消极的后视在某种程度上是可选的,让人们摆脱实际的花括号。

    let R = strs[0];
    args.forEach((arg,i)=> {
        let [_,fmt,str] = strs[i+1].match(/(?:\{(.*)(?<!\\)\})?(.*)/);
        R += FORMATTER(arg,fmt) + str;
    });
于 2021-08-12T22:34:27.470 回答
0

虽然使用模板字符串插值进行格式化不能作为内置功能使用,但您可以通过以下方式获得等效行为Intl.NumberFormat

const format = (num, fraction = 2) => new Intl.NumberFormat([], {
  minimumFractionDigits: fraction,
  maximumFractionDigits: fraction,
}).format(num);

format(5.1234); // -> '5.12'

请注意,无论您选择哪种实现方式,您都可能会被舍入错误所困扰:

(9.999).toFixed(2) // -> '10.00'

new Intl.NumberFormat([], {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2, // <- implicit rounding!
}).format(9.999) // -> '10.00'
于 2018-09-07T15:25:08.337 回答
-2

您可以使用 es6 标签功能。我不知道准备好使用它。

它可能看起来像这样:

num`This is a number: $.2d{n}`

学到更多:

于 2017-04-27T13:22:00.430 回答