应优先使用 intl.numberformat 而非 tolocalestring() 或手写正则,因其原生支持多语言分组规则、符号位置、小数精度及印度式分隔;tolocalestring() 易因漏传 currency 或 locale 不兼容而静默失败或报错。

直接用 Intl.NumberFormat,别手写正则或拼接符号——它能自动处理符号位置、千分位规则、小数精度、甚至印度那种「3,00,00,000」式分隔。
为什么不用 toLocaleString() 就容易出错
Number.prototype.toLocaleString() 看似简单,但默认不带 style: 'currency' 时只做数字分组,不加货币符号;漏传 currency 会抛 RangeError: invalid currency;更隐蔽的是:如果只传 locale 不传 currency,某些浏览器(如旧版 Safari)可能静默忽略格式化,返回原始数字字符串。
正确姿势是始终显式声明三要素:locale、style: 'currency'、currency:
12345.67.toLocaleString('ja-JP', { style: 'currency', currency: 'JPY' }) // → "¥12,346"
12345.67.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }) // → "12 345,67 €"
- 注意空格类型:法语用窄空格(
),不是普通空格 - 日元、韩元等无小数货币,
minimumFractionDigits: 0必须显式设,否则默认显示 .00 - 印度卢比(
INR)的分组是「3,00,00,000」,Intl原生支持,手写正则几乎不可能覆盖
输入框实时格式化的坑:别让 input 事件触发死循环
对 class="currency" 输入框监听 input 事件时,$(this).val(formatted) 会再次触发 input,但只要清洗逻辑够干净,就能天然中断:
- 用
.replace(/[^\d.-]/g, '')提取纯数字字符(保留负号和小数点) -
parseFloat()遇到含符号的格式化字符串(如"¥12,345")直接返回NaN,后续不执行val() - 不要用
keyup:粘贴、语音输入、拖拽数值都不会触发,导致格式滞后 - 光标跳到末尾是副作用,若需保持编辑位置,必须手动保存/恢复
selectionStart,val()本身不提供该能力
服务端渲染 + 客户端补全:避免 SSR 和 CSR 格式不一致
后端(如 Rails 的 number_to_currency)和前端 Intl.NumberFormat 对同一 locale/currency 的输出可能微异:比如德语中千分位是 .、小数位是 ,,但某些旧版 Ruby i18n 配置可能反着来。
稳妥做法是:
- SSR 阶段只输出纯数字(如
<span data-price="12345.67"></span>),不渲染任何格式 - 客户端 JS 拿
data-price值 + 当前document.documentElement.lang调用Intl.NumberFormat渲染 - 若需 fallback,可在 HTML 中预留
lang属性缺失时的兜底 locale(如'en-US')
第三方库如 currencyFormatter.js 的真实代价
OSREC.CurrencyFormatter.format() 确实支持 155 种货币,但体积超 30KB(gzip 后约 12KB),且其 pattern 语法(如 '#,##0.00 !')要额外学习;而原生 Intl 在 Chrome 24+/Firefox 29+/Safari 10+ 全面可用,2026 年已无需 polyfill。
除非你明确需要以下功能,否则没必要引入:
- 运行时动态切换 locale 且要求 IE11 兼容
- 需要自定义千分位符为「|」或「_」等非标准字符
- 服务端 Node.js 环境需同步格式化(此时可选
intl-format-cache优化性能)
最常被忽略的一点:货币格式化结果不可用于计算。显示 "₹ 2,499" 的字符串,必须用原始数字字段参与加减乘除——把格式化后的字符串再 parseFloat 是危险的,会丢失精度或误解析符号。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











