在JavaScript中格式化数字值
数字格式化是在编写网页代码时最常见的展示任务之一。虽然有一些内置的方法,但通常需要为特定的用例自己编写解决方案。让我们探讨一些常见的场景以及如何处理它们。
[!NOTE]
你可能不熟悉JavaScript的数字分隔符,它们在下面的示例中使用。它们是使大型数字值更易读的语法糖。
不带尾随零的定点表示法
在定点表示法中,一个数字用小数点后的固定位数表示。然而,我们经常希望从结果中去除尾随零。
为了做到这一点,我们可以使用Number.prototype.toFixed()
将数字转换为定点表示法字符串。然后,使用Number.parseFloat()
将定点表示法字符串转换为数字,去除尾随零。最后,我们可以使用模板字面量将数字转换为字符串。
const toOptionalFixed = (num, digits) =>
`${Number.parseFloat(num.toFixed(digits))}`;
toOptionalFixed(1, 2); // '1'
toOptionalFixed(1.001, 2); // '1'
toOptionalFixed(1.500, 2); // '1.5'
将数字四舍五入到指定精度
将数字四舍五入到特定的小数位数是非常常见的。我们可以使用Math.round()
和模板字面量将数字四舍五入到指定的位数。省略第二个参数decimals
将四舍五入到整数。
const round = (n, decimals = 0) =>
Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`);
round(1.005, 2); // 1.01
[!NOTE]
此函数返回一个数字,而不是字符串。这是有意的,因为我们可能希望在进一步的计算中使用四舍五入后的数字。
格式化持续时间
将一定数量的毫秒转换为可读格式只需要将该数字除以适当的值,并为每个值创建一个字符串。在下面的代码片段中,我们将使用一个包含day
、hour
、minute
、second
和millisecond
适当值的对象,但您可以根据不同的需求进行适当的调整。
我们可以使用Object.entries()
和Array.prototype.filter()
来保留非零值。然后,我们可以使用Array.prototype.map()
为每个值创建字符串,适当地进行复数化。最后,我们可以使用Array.prototype.join()
将这些值组合成一个字符串。
const formatDuration = ms => {
if (ms < 0) ms = -ms;
const time = {
day: Math.floor(ms / 86_400_000),
hour: Math.floor(ms / 3_600_000) % 24,
minute: Math.floor(ms / 60_000) % 60,
second: Math.floor(ms / 1_000) % 60,
millisecond: Math.floor(ms) % 1_000
};
return Object.entries(time)
.filter(val => val[1] !== 0)
.map(([key, val]) => `${val} ${key}${val !== 1 ? 's' : ''}`)
.join(', ');
};
formatDuration(1_001);
// '1 second, 1 millisecond'
formatDuration(34_325_055_574);
// '397 days, 6 hours, 44 minutes, 15 seconds, 574 milliseconds'
秒数转ISO格式
同样,将秒数格式化为ISO格式也只需要几次除法运算。然而,我们需要单独处理符号。
我们可以使用Array.prototype.map()
与Math.floor()
和String.prototype.padStart()
结合使用,将每个段转换为字符串。最后,我们可以使用Array.prototype.join()
将值组合成一个字符串。
const formatSeconds = s => {
const [hour, minute, second, sign] =
s > 0
? [s / 3_600, (s / 60) % 60, s % 60, '']
: [-s / 3_600, (-s / 60) % 60, -s % 60, '-'];
return (
sign +
[hour, minute, second]
.map(v => `${Math.floor(v)}`.padStart(2, '0'))
.join(':')
);
};
formatSeconds(200); // '00:03:20'
formatSeconds(-200); // '-00:03:20'
formatSeconds(99_999); // '27:46:39'
区域敏感的数字格式化
使用Number.prototype.toLocaleString()
也可以轻松地将数字格式化为区域敏感的字符串。该方法允许我们使用本地数字格式分隔符格式化数字。
const formatNumber = num => num.toLocaleString();
formatNumber(123_456); // 在`en-US`中为'123,456'
formatNumber(15_675_436_903); // 在`de-DE`中为'15.675.436.903'
数字到小数点
如果我们想将数字格式化为特定的区域设置,可以将区域设置作为第一个参数传递给Number.prototype.toLocaleString()
。例如,以下是如何使用en-US
区域设置将数字格式化为使用小数点的方式。
const toDecimalMark = num => num.toLocaleString('en-US');
toDecimalMark(12_305_030_388.9087); // '12,305,030,388.909'
数字转换为货币字符串
在处理货币时,使用适当的格式非常重要。幸运的是,JavaScript的Intl.NumberFormat
使得这一点变得很容易,它允许我们将数字格式化为货币字符串。我们只需要指定货币和语言格式,以及style: 'currency'
。
const toCurrency = (n, curr, LanguageFormat = undefined) =>
Intl.NumberFormat(LanguageFormat, {
style: 'currency',
currency: curr,
}).format(n);
toCurrency(123_456.789, 'EUR');
// €123,456.79 | 货币:欧元 | 货币语言格式:本地
toCurrency(123_456.789, 'USD', 'en-us');
// $123,456.79 | 货币:美元 | 货币语言格式:英语(美国)
toCurrency(123_456.789, 'USD', 'fa');
// ۱۲۳٬۴۵۶٫۷۹ $ | 货币:美元 | 货币语言格式:波斯语
toCurrency(322_342_436_423.2435, 'JPY');
// ¥322,342,436,423 | 货币:日元 | 货币语言格式:本地
toCurrency(322_342_436_423.2435, 'JPY', 'fi');
// 322 342 436 423 ¥ | 货币:日元 | 货币语言格式:芬兰语
数字转换为序数后缀
将数字转换为其序数形式(例如,1
转换为1st
,2
转换为2nd
,3
转换为3rd
等)也相当简单,使用Intl.PluralRules
。我们可以使用Intl.PluralRules.prototype.select()
来获取数字的正确复数形式,然后使用一个查找字典来获取正确的后缀。为了产生正确的结果,我们需要在构造对象时指定正确的区域设置和type: 'ordinal'
选项。
const ordinalsEnUS = {
one: 'st',
two: 'nd',
few: 'rd',
many: 'th',
zero: 'th',
other: 'th',
};
const toOrdinalSuffix = (num, locale = 'en-US', ordinals = ordinalsEnUS) => {
const pluralRules = new Intl.PluralRules(locale, { type: 'ordinal' });
return `${num}${ordinals[pluralRules.select(num)]}`;
};
## 添加序数后缀
有时候,我们需要将数字转换为带有序数后缀的字符串。我们可以使用以下函数来实现:
```js
const toOrdinalSuffix = (n) => {
const suffixes = ['th', 'st', 'nd', 'rd'];
const v = n % 100;
return `${n}${suffixes[(v - 20) % 10] || suffixes[v] || suffixes[0]}`;
};
toOrdinalSuffix(1); // '1st'
toOrdinalSuffix(2); // '2nd'
toOrdinalSuffix(3); // '3rd'
toOrdinalSuffix(4); // '4th'
toOrdinalSuffix(123); // '123rd'
使用前导零填充数字
最后,有时候我们需要将数字填充为指定长度的字符串,并在前面添加零。我们可以使用 String.prototype.padStart()
将数字转换为字符串后,再进行填充。
const padNumber = (n, l) => `${n}`.padStart(l, '0');
padNumber(1_234, 6); // '001234'