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

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

简介

许多应用程序需要以固定的小数位数显示数字。例如,价格通常显示为两位小数,如 $19.99;百分比常以 45.50% 的形式展示;测量值也会采用统一的小数位数以提升可读性,如 3.14 米。

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

本教程将介绍如何将数字格式化为精确的两位小数,或最多两位小数,并兼容不同地区的显示习惯。

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

toFixed() 方法可将数字转换为指定小数位数的字符串。你可以通过参数指定小数位数。

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

该方法始终显示精确的两位小数。如果数字本身小数位数不足,toFixed() 会自动补零;如果小数位数过多,则会四舍五入到两位。

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

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

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

toFixed() 的本地化问题

toFixed() 方法始终使用英文句点作为小数分隔符,无论用户的地区设置如何。而在许多国家,小数分隔符使用逗号而不是句点。

const price = 19.99;
console.log(price.toFixed(2));
// Output: "19.99" (always uses period)

对于德国、法国、西班牙以及许多其他国家的用户来说,这样的显示方式是错误的。他们期望看到 "19,99",而不是 "19.99"。toFixed() 方法无法生成符合本地习惯的输出。

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

使用 Intl.NumberFormat 进行本地化数字格式化

Intl.NumberFormat API 会根据本地化习惯格式化数字。创建格式化器时指定 locale 和 options,然后用 format() 方法传入数字即可。

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

const price = 19.99;
console.log(formatter.format(price));
// Output: "19,99" (uses comma for German locale)

同一个格式化器在不同的本地化环境下会生成不同的输出。

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));
// Output: "19.99"

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

小数点分隔符会根据本地化环境自动变化。

格式化为严格两位小数

如需显示严格的两位小数,请将 minimumFractionDigitsmaximumFractionDigits 都设置为 2。这样可以确保输出始终为两位小数,不足时补零,超出时四舍五入。

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

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

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

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

minimumFractionDigits 选项用于控制末尾的零。如果不设置该选项,位数不足的小数不会显示补零。

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

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

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

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

将两个选项都设置为相同的值,可以保证所有数字的小数位数一致。

格式化为最多两位小数

有时你只希望在需要时显示小数位,最多保留两位。此时将 maximumFractionDigits 设为 2,minimumFractionDigits 设为 0 或直接省略。

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

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

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

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

这种方式会去除末尾的零,但仍然限制精度为两位小数。适用于显示测量值或统计数据等场景,末尾零没有实际信息时效果更佳。

minimumFractionDigits 的默认值为 0(用于普通数字格式化),因此可以省略该参数。

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

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

console.log(formatter.format(5.5));
// Output: "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));
});
// Output:
// "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));
// Output varies by user's locale
// For en-US: "19.99"
// For de-DE: "19,99"
// For fr-FR: "19,99"

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

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

如果用户的首选项不被支持,这种方式会自动回退到下一个可用选项。