如何在 JavaScript 中将数字格式化为两位小数

为价格、百分比和测量值显示精确到两位小数的数字

介绍

许多应用程序需要以固定的小数位数显示数字。例如,价格通常显示为两位小数,如 $19.99。百分比通常显示为 45.50%。测量值为了便于阅读也会使用一致的小数位数,如 3.14 米。

控制小数位数可以确保应用程序中视觉上的一致性。如果没有明确的格式化,JavaScript 会根据实际值显示不同的小数位数。例如,数字 5 显示为 "5",而 5.5 显示为 "5.5",这会导致对齐和外观的不一致。

本课程将展示如何将数字格式化为精确的 2 位小数,或最多 2 位小数,同时尊重不同语言环境的约定。

使用 toFixed() 进行基本格式化

toFixed() 方法将数字转换为具有指定小数位数的字符串。将小数位数作为参数传递。

const price = 19.9;
const formatted = price.toFixed(2);
console.log(formatted);
// 输出: "19.90"

该方法始终显示精确的 2 位小数。如果数字的小数位数少于 2 位,toFixed() 会用零填充。如果小数位数多于 2 位,该方法会四舍五入到 2 位。

const examples = [5, 5.5, 5.555, 5.999];

examples.forEach(num => {
  console.log(num.toFixed(2));
});
// 输出:
// "5.00"
// "5.50"
// "5.56"
// "6.00"

toFixed() 方法返回的是字符串,而不是数字。这是有意为之,因为尾随的零对于显示有意义,但如果返回数字则会丢失。

toFixed() 的语言环境问题

toFixed() 方法始终使用句点作为小数分隔符,而不考虑用户的语言环境。许多国家使用逗号作为小数分隔符,而不是句点。

const price = 19.99;
console.log(price.toFixed(2));
// 输出: "19.99" (始终使用句点)

对于德国、法国、西班牙和许多其他国家的用户来说,这看起来是错误的。他们期望看到 "19,99" 而不是 "19.99"。toFixed() 方法无法生成符合语言环境的输出。

要为不同的语言环境正确格式化数字,请使用 Intl.NumberFormat API。

使用 Intl.NumberFormat 进行符合本地习惯的格式化

Intl.NumberFormat API 根据本地化习惯格式化数字。通过指定一个语言环境和选项创建格式化器,然后使用数字调用其 format() 方法。

const formatter = new Intl.NumberFormat("de-DE", {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
});

const price = 19.99;
console.log(formatter.format(price));
// 输出: "19,99" (德语环境使用逗号作为小数点)

相同的格式化器在不同的语言环境下会产生不同的输出。

const price = 19.99;

const usFormatter = new Intl.NumberFormat("en-US", {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
});

const deFormatter = new Intl.NumberFormat("de-DE", {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
});

console.log(usFormatter.format(price));
// 输出: "19.99"

console.log(deFormatter.format(price));
// 输出: "19,99"

小数点分隔符会根据语言环境自动更改。

格式化为精确的两位小数

要显示精确的两位小数,请将 minimumFractionDigitsmaximumFractionDigits 都设置为 2。这确保输出始终有两位小数,必要时用零填充,并在数字精度更高时进行四舍五入。

const formatter = new Intl.NumberFormat("en-US", {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
});

console.log(formatter.format(5));
// 输出: "5.00"

console.log(formatter.format(5.5));
// 输出: "5.50"

console.log(formatter.format(5.555));
// 输出: "5.56"

minimumFractionDigits 选项控制尾随零。如果不设置,位数较少的小数将不会显示零。

const withoutMinimum = new Intl.NumberFormat("en-US", {
  maximumFractionDigits: 2
});

console.log(withoutMinimum.format(5));
// 输出: "5"

const withMinimum = new Intl.NumberFormat("en-US", {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
});

console.log(withMinimum.format(5));
// 输出: "5.00"

将两个选项设置为相同的值可以确保所有数字的十进制位数一致。

格式化为最多两位小数

有时您只希望在需要时显示小数位,最多显示两位。将 maximumFractionDigits 设置为 2,并将 minimumFractionDigits 设置为 0 或完全省略。

const formatter = new Intl.NumberFormat("en-US", {
  minimumFractionDigits: 0,
  maximumFractionDigits: 2
});

console.log(formatter.format(5));
// 输出: "5"

console.log(formatter.format(5.5));
// 输出: "5.5"

console.log(formatter.format(5.555));
// 输出: "5.56"

这种方法去除了尾随零,但仍将精度限制为两位小数。它非常适合显示测量值或统计数据,其中尾随零没有信息价值。

对于普通数字格式化,minimumFractionDigits 的默认值为 0,因此可以省略。

const formatter = new Intl.NumberFormat("en-US", {
  maximumFractionDigits: 2
});

console.log(formatter.format(5));
// 输出: "5"

console.log(formatter.format(5.5));
// 输出: "5.5"

重用格式化器以提高性能

创建一个新的 Intl.NumberFormat 实例的开销相对较大。如果需要使用相同的选项格式化多个数字,请只创建一次格式化器并重复使用。

const formatter = new Intl.NumberFormat("en-US", {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
});

const prices = [19.99, 29.5, 99, 149.999];

prices.forEach(price => {
  console.log(formatter.format(price));
});
// 输出:
// "19.99"
// "29.50"
// "99.00"
// "150.00"

这种模式比为每个数字创建一个新的格式化器更高效。

何时使用每种方法

在显示价格、货币金额或小数位具有语义意义的任何值时,使用精确到 2 位小数的格式。显示 "$5.00" 而不是 "$5" 表明了精确性,并符合用户对价格的预期。

在显示统计数据、测量值或计算值时,使用最多 2 位小数的格式,其中尾随零不提供额外信息。显示 "5 米" 而不是 "5.00 米" 更简洁且更易读。

仅在确定所有用户都使用相同的小数分隔符约定时,或输出不面向用户时,使用 toFixed()。对于国际化应用程序,优先使用 Intl.NumberFormat

使用用户的首选区域设置

与其硬编码区域设置,不如使用用户的浏览器语言偏好。navigator.language 属性提供了用户的首选区域设置。

const formatter = new Intl.NumberFormat(navigator.language, {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
});

const price = 19.99;
console.log(formatter.format(price));
// 输出因用户的区域设置而异
// 对于 en-US: "19.99"
// 对于 de-DE: "19,99"
// 对于 fr-FR: "19,99"

您还可以传递整个 navigator.languages 数组,让 Intl API 从用户的偏好中选择第一个支持的区域设置。

const formatter = new Intl.NumberFormat(navigator.languages, {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
});

这种方法在用户的首选项不受支持时提供了自动回退。