模板字符串中的数字格式(Javascript - ES6)
Number formatting in template strings (Javascript - ES6)
我想知道是否可以在 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
该语法显然不起作用,它只是对我正在寻找的事物类型的说明。
我知道 underscore.string
中的 sprintf
等工具,但这似乎是 JS 应该能够开箱即用的东西,特别是考虑到模板字符串的强大功能。
编辑
如上所述,我已经知道第 3 方工具(例如 sprintf)和自定义函数可以执行此操作。类似的问题(例如 JavaScript equivalent to printf/String.Format)根本没有提到模板字符串,可能是因为在 ES6 模板字符串出现之前就有人问过它们。我的问题是针对 ES6 的,并且与实现无关。如果是这种情况,我很乐意接受 "No, this is not possible" 的回答,但最好的是关于提供此功能的新 ES6 功能的信息,或者对此类功能是否即将推出的一些见解。
您应该可以使用数字的 toFixed() 方法:
var num = 5.1234;
var n = num.toFixed(2);
不,ES6 没有引入任何新的数字格式化函数,不过您将不得不使用现有的 .toExponential(fractionDigits)
, .toFixed(fractionDigits)
, .toPrecision(precision)
, .toString([radix])
and toLocaleString(…)
(which has been updated to optionally support the ECMA-402 Standard。
模板字符串与数字格式无关,它们只是将函数调用(如果标记)或字符串连接(默认)脱糖。
如果这些 Number
方法对您来说还不够,您将不得不自己动手。如果您愿意,当然可以将格式化函数编写为模板字符串标记。
你可以使用es6标签函数。我不知道是否可以使用它。
它可能看起来像这样:
num`This is a number: $.2d{n}`
了解更多:
如果你想使用 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}`;
这将格式化浮点数并单独保留字符串。
虽然使用模板字符串插值进行格式化不作为内置功能提供,但您可以使用 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'
这是上面 Filip Allberg 解决方案的完整 ES6 版本,使用 ES6 "rest" 参数。唯一缺少的是能够改变精度;这可以通过制作工厂功能来完成。留作 reader.
的练习
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})`);
基于 ES6 Tagged Templates (credit to ),这将模拟其他语言中的典型模板字符串语法(这大致基于 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;
}
sidenote: 代码核心如下。繁重的工作由格式化程序完成。负向回顾在某种程度上是可选的,并且让人们逃避实际的大括号。
let R = strs[0];
args.forEach((arg,i)=> {
let [_,fmt,str] = strs[i+1].match(/(?:\{(.*)(?<!\)\})?(.*)/);
R += FORMATTER(arg,fmt) + str;
});
我想知道是否可以在 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
该语法显然不起作用,它只是对我正在寻找的事物类型的说明。
我知道 underscore.string
中的 sprintf
等工具,但这似乎是 JS 应该能够开箱即用的东西,特别是考虑到模板字符串的强大功能。
编辑
如上所述,我已经知道第 3 方工具(例如 sprintf)和自定义函数可以执行此操作。类似的问题(例如 JavaScript equivalent to printf/String.Format)根本没有提到模板字符串,可能是因为在 ES6 模板字符串出现之前就有人问过它们。我的问题是针对 ES6 的,并且与实现无关。如果是这种情况,我很乐意接受 "No, this is not possible" 的回答,但最好的是关于提供此功能的新 ES6 功能的信息,或者对此类功能是否即将推出的一些见解。
您应该可以使用数字的 toFixed() 方法:
var num = 5.1234;
var n = num.toFixed(2);
不,ES6 没有引入任何新的数字格式化函数,不过您将不得不使用现有的 .toExponential(fractionDigits)
, .toFixed(fractionDigits)
, .toPrecision(precision)
, .toString([radix])
and toLocaleString(…)
(which has been updated to optionally support the ECMA-402 Standard。
模板字符串与数字格式无关,它们只是将函数调用(如果标记)或字符串连接(默认)脱糖。
如果这些 Number
方法对您来说还不够,您将不得不自己动手。如果您愿意,当然可以将格式化函数编写为模板字符串标记。
你可以使用es6标签函数。我不知道是否可以使用它。
它可能看起来像这样:
num`This is a number: $.2d{n}`
了解更多:
如果你想使用 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}`;
这将格式化浮点数并单独保留字符串。
虽然使用模板字符串插值进行格式化不作为内置功能提供,但您可以使用 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'
这是上面 Filip Allberg 解决方案的完整 ES6 版本,使用 ES6 "rest" 参数。唯一缺少的是能够改变精度;这可以通过制作工厂功能来完成。留作 reader.
的练习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})`);
基于 ES6 Tagged Templates (credit to 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;
}
sidenote: 代码核心如下。繁重的工作由格式化程序完成。负向回顾在某种程度上是可选的,并且让人们逃避实际的大括号。
let R = strs[0];
args.forEach((arg,i)=> {
let [_,fmt,str] = strs[i+1].match(/(?:\{(.*)(?<!\)\})?(.*)/);
R += FORMATTER(arg,fmt) + str;
});