模板字符串中的数字格式(Javascript - ES6)
Posted
技术标签:
【中文标题】模板字符串中的数字格式(Javascript - ES6)【英文标题】:Number formatting in template strings (Javascript - ES6) 【发布时间】:2015-10-23 03:10:54 【问题描述】:我想知道是否可以在 javascript 模板字符串中格式化数字,例如:
var n = 5.1234;
console.log(`This is a number: $.2dn`);
// -> 5.12
或者可能
var n = 5.1234;
console.log(`This is a number: $n.toString('.2d')`);
// -> 5.12
该语法显然不起作用,它只是我正在寻找的事物类型的说明。
我知道underscore.string
中的sprintf
之类的工具,但这似乎是JS 应该能够开箱即用的东西,尤其是考虑到模板字符串的强大功能。
编辑
如上所述,我已经知道 3rd 方工具(例如 sprintf)和自定义函数来执行此操作。类似的问题(例如JavaScript equivalent to printf/String.Format)根本没有提到模板字符串,可能是因为在 ES6 模板字符串出现之前就已经问过了。我的问题是 ES6 特有的,并且与实现无关。如果是这种情况,我很高兴接受“不,这是不可能的”的回答,但是最好的是有关提供此功能的新 ES6 功能的信息,或者对此类功能是否存在的一些见解方式。
【问题讨论】:
在 Adobe JavaScript 中是可能的。util.printf
否则我们必须等到 ECMAScript 6 可用...
@WhiteHat - 检查你的日历
是的,但我在哪里可以使用它? IE 9 是我工作的标准问题,只是在那里有点抱怨。
【参考方案1】:
基于ES6 Tagged Templates(归功于https://***.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/3d ~ $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/3d -> 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;
旁注:代码的核心如下。繁重的工作由格式化程序完成。否定的lookbehind在某种程度上是可选的,可以让人们避开实际的花括号。
let R = strs[0];
args.forEach((arg,i)=>
let [_,fmt,str] = strs[i+1].match(/(?:\(.*)(?<!\\)\)?(.*)/);
R += FORMATTER(arg,fmt) + str;
);
【讨论】:
【参考方案2】:这是上面 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)`);
【讨论】:
【参考方案3】:虽然使用模板字符串插值进行格式化不能作为内置功能使用,但您可以通过 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'
【讨论】:
【参考方案4】:如果你想使用 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`;
这将格式化浮点数并单独保留字符串。
【讨论】:
不错!这应该是公认的答案。这是一个带有“rest”参数的完全 ES6 版本:codepen.io/garyo/pen/gZYmNP【参考方案5】:你应该可以使用数字的 toFixed() 方法:
var num = 5.1234;
var n = num.toFixed(2);
【讨论】:
【参考方案6】:您可以使用 es6 标签函数。我不知道准备好使用它。
它可能看起来像这样:
num`This is a number: $.2dn`
了解更多:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals https://developers.google.com/web/updates/2015/01/ES6-Template-Strings【讨论】:
这首先给了我一个语法错误,num 没有定义,如果我从它的开头删除“num”也不起作用。这里有详细信息吗? 一个完整的例子会很好。然而,MDN 链接确实很好地解释了它。【参考方案7】:不,ES6 没有引入任何新的数字格式化功能,您将不得不使用现有的 .toExponential(fractionDigits)
、.toFixed(fractionDigits)
、.toPrecision(precision)
、.toString([radix])
和 toLocaleString(…)
(已更新为可选支持ECMA-402 Standard,不过)。
模板字符串与数字格式无关,它们只是对函数调用(如果已标记)或字符串连接(默认)脱糖。
如果这些 Number
方法对您来说还不够,您将不得不自己动手。如果您愿意,当然可以将格式化函数编写为模板字符串标记。
【讨论】:
以上是关于模板字符串中的数字格式(Javascript - ES6)的主要内容,如果未能解决你的问题,请参考以下文章