2025年3月7日大约 10 分钟
我将为您完善数字与货币格式化的文档,详细介绍 Intl.NumberFormat 的用法和选项。
---
title: 数字与货币格式化
icon: javascript
order: 3
---
# 数字与货币格式化
Intl.NumberFormat对象用于格式化数字,包括货币、百分比和单位。本文将详细介绍NumberFormat的选项和用法,帮助您实现符合各地区习惯的数字显示。
## Intl.NumberFormat 基础
`Intl.NumberFormat` 是 ECMAScript 国际化 API 的一部分,专门用于根据语言和地区的规则格式化数字。它可以处理各种数字格式,包括普通数字、货币、百分比和带单位的数字。
### 基本语法
```javascript
new Intl.NumberFormat([locales[, options]])
locales
:可选,指定一个或多个语言标签(如 'zh-CN'、'en-US')options
:可选,一个对象,包含各种格式化选项
简单示例
// 使用默认区域设置格式化数字
const number = 123456.789;
const formatter = new Intl.NumberFormat();
console.log(formatter.format(number)); // 例如:123,456.789
// 使用特定区域设置
const cnFormatter = new Intl.NumberFormat('zh-CN');
console.log(cnFormatter.format(number)); // 例如:123,456.789
const deFormatter = new Intl.NumberFormat('de-DE');
console.log(deFormatter.format(number)); // 例如:123.456,789
格式化选项
Intl.NumberFormat
提供了丰富的选项来自定义数字的格式。
基本数字格式化
可以控制数字的基本格式,如小数位数、千位分隔符等:
const number = 123456.789;
// 控制小数位数
const formatter1 = new Intl.NumberFormat('zh-CN', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
console.log(formatter1.format(number)); // 123,456.79
// 控制整数位数
const formatter2 = new Intl.NumberFormat('zh-CN', {
minimumIntegerDigits: 8
});
console.log(formatter2.format(number)); // 00,123,456.789
// 禁用千位分隔符
const formatter3 = new Intl.NumberFormat('zh-CN', {
useGrouping: false
});
console.log(formatter3.format(number)); // 123456.789
货币格式化
格式化货币金额,自动应用适当的货币符号和格式:
const amount = 123456.789;
// 基本货币格式化
const currencyFormatter = new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
});
console.log(currencyFormatter.format(amount)); // ¥123,456.79
// 不同货币和区域设置
const usdFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
console.log(usdFormatter.format(amount)); // $123,456.79
const eurFormatter = new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
});
console.log(eurFormatter.format(amount)); // 123.456,79 €
// 控制货币显示方式
const currencyCodeFormatter = new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'USD',
currencyDisplay: 'code' // 'symbol', 'code', 'name'
});
console.log(currencyCodeFormatter.format(amount)); // USD 123,456.79
const currencyNameFormatter = new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'USD',
currencyDisplay: 'name'
});
console.log(currencyNameFormatter.format(amount)); // 123,456.79美元
百分比格式化
将数字格式化为百分比:
const ratio = 0.1234;
// 基本百分比格式化
const percentFormatter = new Intl.NumberFormat('zh-CN', {
style: 'percent'
});
console.log(percentFormatter.format(ratio)); // 12%
// 控制小数位数
const precisePercentFormatter = new Intl.NumberFormat('zh-CN', {
style: 'percent',
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
console.log(precisePercentFormatter.format(ratio)); // 12.34%
单位格式化
格式化带有单位的数字(ES2020 新特性):
const length = 5;
// 基本单位格式化
const meterFormatter = new Intl.NumberFormat('zh-CN', {
style: 'unit',
unit: 'meter'
});
console.log(meterFormatter.format(length)); // 5米
// 控制单位显示方式
const meterLongFormatter = new Intl.NumberFormat('zh-CN', {
style: 'unit',
unit: 'meter',
unitDisplay: 'long' // 'narrow', 'short', 'long'
});
console.log(meterLongFormatter.format(length)); // 5米
// 不同单位
const temperatureFormatter = new Intl.NumberFormat('zh-CN', {
style: 'unit',
unit: 'celsius',
unitDisplay: 'long'
});
console.log(temperatureFormatter.format(25)); // 25摄氏度
// 复合单位
const speedFormatter = new Intl.NumberFormat('zh-CN', {
style: 'unit',
unit: 'kilometer-per-hour',
unitDisplay: 'long'
});
console.log(speedFormatter.format(100)); // 100公里每小时
记数法选项
控制数字的记数法(ES2020 新特性):
const largeNumber = 1234567;
// 科学记数法
const scientificFormatter = new Intl.NumberFormat('zh-CN', {
notation: 'scientific'
});
console.log(scientificFormatter.format(largeNumber)); // 1.234567E6
// 工程记数法
const engineeringFormatter = new Intl.NumberFormat('zh-CN', {
notation: 'engineering'
});
console.log(engineeringFormatter.format(largeNumber)); // 1.234567E6
// 紧凑记数法
const compactFormatter = new Intl.NumberFormat('zh-CN', {
notation: 'compact',
compactDisplay: 'short' // 'short', 'long'
});
console.log(compactFormatter.format(largeNumber)); // 123万
// 紧凑记数法(长格式)
const compactLongFormatter = new Intl.NumberFormat('zh-CN', {
notation: 'compact',
compactDisplay: 'long'
});
console.log(compactLongFormatter.format(largeNumber)); // 123万
有效数字
控制有效数字的位数:
const number = 123.456789;
// 控制有效数字
const significantDigitsFormatter = new Intl.NumberFormat('zh-CN', {
maximumSignificantDigits: 3
});
console.log(significantDigitsFormatter.format(number)); // 123
const preciseSignificantDigitsFormatter = new Intl.NumberFormat('zh-CN', {
minimumSignificantDigits: 6,
maximumSignificantDigits: 6
});
console.log(preciseSignificantDigitsFormatter.format(number)); // 123.457
舍入方式
控制数字的舍入方式(ES2022 新特性):
const number = 1.5;
// 不同的舍入方式
const ceilFormatter = new Intl.NumberFormat('zh-CN', {
maximumFractionDigits: 0,
roundingMode: 'ceil' // 向上舍入
});
console.log(ceilFormatter.format(number)); // 2
const floorFormatter = new Intl.NumberFormat('zh-CN', {
maximumFractionDigits: 0,
roundingMode: 'floor' // 向下舍入
});
console.log(floorFormatter.format(number)); // 1
const expandFormatter = new Intl.NumberFormat('zh-CN', {
maximumFractionDigits: 0,
roundingMode: 'expand' // 向远离零方向舍入
});
console.log(expandFormatter.format(number)); // 2
console.log(expandFormatter.format(-1.5)); // -2
const trunkFormatter = new Intl.NumberFormat('zh-CN', {
maximumFractionDigits: 0,
roundingMode: 'trunc' // 向零方向舍入
});
console.log(trunkFormatter.format(number)); // 1
console.log(trunkFormatter.format(-1.5)); // -1
格式化方法
format()
最基本的格式化方法,返回格式化后的字符串:
const number = 123456.789;
const formatter = new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' });
console.log(formatter.format(number)); // ¥123,456.79
formatToParts()
返回一个数组,包含格式化后的各个部分:
const number = 123456.789;
const formatter = new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
});
const parts = formatter.formatToParts(number);
console.log(parts);
// 输出类似:
// [
// { type: 'currency', value: '¥' },
// { type: 'integer', value: '123' },
// { type: 'group', value: ',' },
// { type: 'integer', value: '456' },
// { type: 'decimal', value: '.' },
// { type: 'fraction', value: '79' }
// ]
// 可以用于自定义格式
const integer = parts
.filter(part => part.type === 'integer')
.map(part => part.value)
.join('');
const fraction = parts.find(part => part.type === 'fraction')?.value || '';
console.log(`整数部分:${integer},小数部分:${fraction}`); // 整数部分:123456,小数部分:79
formatRange() 和 formatRangeToParts()
ES2023 新特性,用于格式化数字范围:
// 注意:这是较新的特性,可能需要检查浏览器兼容性
const formatter = new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
});
// 格式化数字范围
if (formatter.formatRange) {
console.log(formatter.formatRange(3, 5)); // ¥3.00–¥5.00
console.log(formatter.formatRange(3000, 5000)); // ¥3,000.00–¥5,000.00
}
// 获取范围的各个部分
if (formatter.formatRangeToParts) {
const parts = formatter.formatRangeToParts(3, 5);
console.log(parts);
// 输出类似:
// [
// { type: 'currency', value: '¥', source: 'startRange' },
// { type: 'integer', value: '3', source: 'startRange' },
// { type: 'decimal', value: '.', source: 'startRange' },
// { type: 'fraction', value: '00', source: 'startRange' },
// { type: 'literal', value: '–', source: 'shared' },
// { type: 'currency', value: '¥', source: 'endRange' },
// { type: 'integer', value: '5', source: 'endRange' },
// { type: 'decimal', value: '.', source: 'endRange' },
// { type: 'fraction', value: '00', source: 'endRange' }
// ]
}
实际应用示例
价格显示
创建一个函数,根据不同的区域设置和货币格式化价格:
function formatPrice(price, currency, locale) {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency,
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(price);
}
// 使用示例
console.log(formatPrice(1234.5, 'CNY', 'zh-CN')); // ¥1,234.50
console.log(formatPrice(1234.5, 'USD', 'en-US')); // $1,234.50
console.log(formatPrice(1234.5, 'EUR', 'de-DE')); // 1.234,50 €
console.log(formatPrice(1234.5, 'JPY', 'ja-JP')); // ¥1,235
大数字的友好显示
创建一个函数,以友好的方式显示大数字:
function formatLargeNumber(number, locale = 'zh-CN') {
if (Math.abs(number) >= 1e9) {
return new Intl.NumberFormat(locale, {
notation: 'compact',
compactDisplay: 'long',
maximumFractionDigits: 1
}).format(number);
} else if (Math.abs(number) >= 1e6) {
return new Intl.NumberFormat(locale, {
notation: 'compact',
compactDisplay: 'short',
maximumFractionDigits: 1
}).format(number);
} else if (Math.abs(number) >= 1e3) {
return new Intl.NumberFormat(locale, {
maximumFractionDigits: 0
}).format(number);
} else {
return new Intl.NumberFormat(locale, {
maximumFractionDigits: 2
}).format(number);
}
}
// 使用示例
console.log(formatLargeNumber(123)); // 123
console.log(formatLargeNumber(1234)); // 1,234
console.log(formatLargeNumber(1234567)); // 123万
console.log(formatLargeNumber(1234567890)); // 12.3亿
文件大小格式化
创建一个函数,以友好的方式显示文件大小:
function formatFileSize(bytes, locale = 'zh-CN') {
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
let size = bytes;
let unitIndex = 0;
// 找到合适的单位
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
// 格式化数字
const formatter = new Intl.NumberFormat(locale, {
maximumFractionDigits: unitIndex === 0 ? 0 : 2
});
return `${formatter.format(size)} ${units[unitIndex]}`;
}
// 使用示例
console.log(formatFileSize(1023)); // 1,023 B
console.log(formatFileSize(1024)); // 1 KB
console.log(formatFileSize(1536)); // 1.5 KB
console.log(formatFileSize(1048576)); // 1 MB
console.log(formatFileSize(1073741824)); // 1 GB
数字范围格式化
创建一个函数,格式化数字范围:
function formatNumberRange(min, max, options = {}, locale = 'zh-CN') {
const formatter = new Intl.NumberFormat(locale, options);
// 检查是否支持 formatRange
if (typeof formatter.formatRange === 'function') {
return formatter.formatRange(min, max);
}
// 回退方案:分别格式化两个数字
return `${formatter.format(min)} - ${formatter.format(max)}`;
}
// 使用示例
console.log(formatNumberRange(1000, 2000)); // 1,000 - 2,000
console.log(formatNumberRange(1000, 2000, { style: 'currency', currency: 'CNY' })); // ¥1,000.00 - ¥2,000.00
console.log(formatNumberRange(0.1, 0.2, { style: 'percent' })); // 10% - 20%
价格区间显示
创建一个函数,显示价格区间:
function formatPriceRange(minPrice, maxPrice, currency, locale = 'zh-CN') {
const options = {
style: 'currency',
currency: currency
};
// 如果最小价格和最大价格相同
if (minPrice === maxPrice) {
return new Intl.NumberFormat(locale, options).format(minPrice);
}
// 如果支持 formatRange
const formatter = new Intl.NumberFormat(locale, options);
if (typeof formatter.formatRange === 'function') {
return formatter.formatRange(minPrice, maxPrice);
}
// 回退方案
return `${formatter.format(minPrice)} - ${formatter.format(maxPrice)}`;
}
// 使用示例
console.log(formatPriceRange(100, 100, 'CNY')); // ¥100.00
console.log(formatPriceRange(100, 200, 'CNY')); // ¥100.00 - ¥200.00
console.log(formatPriceRange(99.5, 199.5, 'USD', 'en-US')); // $99.50 - $199.50
数字输入格式化
创建一个函数,在用户输入数字时进行实时格式化:
function createNumberInputFormatter(locale = 'zh-CN', options = {}) {
const formatter = new Intl.NumberFormat(locale, options);
// 格式化输入
function formatInput(input) {
// 移除非数字字符(保留小数点和负号)
const numericValue = input.replace(/[^\d.-]/g, '');
// 如果为空或只有负号,直接返回
if (numericValue === '' || numericValue === '-') {
return numericValue;
}
// 尝试解析为数字
const number = parseFloat(numericValue);
if (isNaN(number)) {
return '';
}
return formatter.format(number);
}
// 解析格式化的值
function parseFormattedValue(formattedValue) {
// 移除所有非数字字符(保留小数点和负号)
const numericString = formattedValue.replace(/[^\d.-]/g, '');
return parseFloat(numericString);
}
return {
format: formatInput,
parse: parseFormattedValue
};
}
// 使用示例(在浏览器环境中)
// const inputFormatter = createNumberInputFormatter('zh-CN', {
// maximumFractionDigits: 2
// });
//
// // 假设有一个输入框
// const input = document.getElementById('number-input');
//
// input.addEventListener('input', (e) => {
// const cursorPosition = e.target.selectionStart;
// const oldLength = e.target.value.length;
//
// // 格式化输入值
// e.target.value = inputFormatter.format(e.target.value);
//
// // 调整光标位置
// const newLength = e.target.value.length;
// const newPosition = cursorPosition + (newLength - oldLength);
// e.target.setSelectionRange(newPosition, newPosition);
// });
//
// // 获取实际数值
// function getNumericValue() {
// return inputFormatter.parse(input.value);
// }
统计数据格式化
创建一个函数,格式化统计数据:
function formatStatistics(stats, locale = 'zh-CN') {
const numberFormatter = new Intl.NumberFormat(locale);
const percentFormatter = new Intl.NumberFormat(locale, {
style: 'percent',
maximumFractionDigits: 1
});
const currencyFormatter = new Intl.NumberFormat(locale, {
style: 'currency',
currency: 'CNY'
});
return {
users: numberFormatter.format(stats.users),
activeUsers: numberFormatter.format(stats.activeUsers),
activeRate: percentFormatter.format(stats.activeUsers / stats.users),
revenue: currencyFormatter.format(stats.revenue),
averageRevenue: currencyFormatter.format(stats.revenue / stats.users)
};
}
// 使用示例
const stats = {
users: 1234567,
activeUsers: 987654,
revenue: 9876543.21
};
const formattedStats = formatStatistics(stats);
console.log(`总用户数: ${formattedStats.users}`); // 总用户数: 1,234,567
console.log(`活跃用户: ${formattedStats.activeUsers}`); // 活跃用户: 987,654
console.log(`活跃率: ${formattedStats.activeRate}`); // 活跃率: 80.0%
console.log(`总收入: ${formattedStats.revenue}`); // 总收入: ¥9,876,543.21
console.log(`人均收入: ${formattedStats.averageRevenue}`); // 人均收入: ¥8.00
性能优化
缓存格式化器实例
为了提高性能,可以缓存 Intl.NumberFormat
实例:
// 创建格式化器缓存
const formatterCache = new Map();
function getCachedNumberFormatter(locale, options) {
// 创建缓存键
const key = locale + JSON.stringify(options || {});
// 检查缓存
if (!formatterCache.has(key)) {
formatterCache.set(key, new Intl.NumberFormat(locale, options));
}
return formatterCache.get(key);
}
// 使用缓存的格式化器
function formatNumberCached(number, locale, options) {
const formatter = getCachedNumberFormatter(locale, options);
return formatter.format(number);
}
// 性能测试
function performanceTest() {
const number = 123456.789;
const options = { style: 'currency', currency: 'CNY' };
const iterations = 1000;
console.time('不缓存');
for (let i = 0; i < iterations; i++) {
new Intl.NumberFormat('zh-CN', options).format(number);
}
console.timeEnd('不缓存');
console.time('使用缓存');
for (let i = 0; i < iterations; i++) {
formatNumberCached(number, 'zh-CN', options);
}
console.timeEnd('使用缓存');
}
// performanceTest();
// 输出类似:
// 不缓存: 120ms
// 使用缓存: 3ms
浏览器兼容性
Intl.NumberFormat
在现代浏览器中得到了广泛支持,但不同的浏览器和版本对特定功能的支持可能有所不同:
- 基本的
Intl.NumberFormat
功能在所有现代浏览器中都有良好支持 unit
样式和notation
选项在较新的浏览器版本中支持formatRange
和formatRangeToParts
方法是较新的添加,可能需要检查兼容性roundingMode
选项是 ES2022 的新特性,支持可能有限
可以使用特性检测来确保代码在不支持某些功能的浏览器中仍能正常工作:
function isUnitFormatSupported() {
try {
new Intl.NumberFormat('en', { style: 'unit', unit: 'meter' }).format(5);
return true;
} catch (e) {
return false;
}
}
function formatWithUnit(number, unit, locale) {
if (isUnitFormatSupported()) {
return new Intl.NumberFormat(locale, {
style: 'unit',
unit: unit
}).format(number);
}
// 回退方案
return `${new Intl.NumberFormat(locale).format(number)} ${unit}`;
}
总结
Intl.NumberFormat
是一个强大的工具,用于根据不同语言和地区的规则格式化数字。它提供了丰富的选项和方法,可以满足各种数字格式化需求,包括货币、百分比、单位等。
主要优点包括:
- 国际化支持:自动处理不同语言和地区的数字格式
- 灵活的格式化选项:可以自定义数字的各个方面,如小数位数、千位分隔符等
- 多种格式样式:支持普通数字、货币、百分比和带单位的数字
- 现代记数法:支持科学记数法、工程记数法和紧凑记数法
- 精确控制:可以控制舍入方式、有效数字等
通过掌握 Intl.NumberFormat
,开发者可以轻松实现多语言数字格式化,为全球用户提供本地化的体验。随着 Web 应用程序的全球化,Intl.NumberFormat
将成为前端开发者工具箱中越来越重要的工具。