点赞 + 关注 + 收藏 = 学会了
在做前端开发或者使用 n8n、dify 等工具时可能会跟数字打交道,可能会遇到下面这些需求:
- 显示金额:
1234567.89 → 1,234,567.89 - 金额计算:
0.1 + 0.2 - 超大 ID:
9007199254740993 - 阿拉伯数字转中文金额:
123456.78 → 壹拾贰万叁仟肆佰伍拾陆元柒角捌分
很多刚接触 JavaScript 的开发者会直接使用 Number 类型处理这些问题,但实际上这里面隐藏了不少 坑。
举个例子:
如果后端传给你的 JSON 里的长 ID 没加引号,前端在 JSON.parse 的一瞬间就已经把精度丢了。
const rawJson = '{"id": 9007199254740995}';
const parsed = JSON.parse(rawJson);
console.log(parsed.id.toString()); // "9007199254740996" 精度丢失了!!!
解决方案:
- 后端改 String:最省心的办法。让后端把超长 ID 以字符串形式下发。
- 前端插件:如果后端不改,你可以使用
json-bigint库来解析原始的 JSON 字符串。
数值是否安全?
在实际应用中,可以通过一些方法来胖段当前数值是否安全。
JS 提供的方法:
Number.isSafeInteger(9007199254740991) // true
Number.isSafeInteger(9007199254740992) // false
处理大数值的几种方案
在 JavaScript 里,普通数字类型是 Number(64位双精度浮点),它有一个安全整数范围:
- 最大安全整数:
Number.MAX_SAFE_INTEGER= 9007199254740991 - 最小安全整数:
Number.MIN_SAFE_INTEGER= -9007199254740991
超过这个范围就会出现 精度丢失问题,例如:
console.log(9007199254740991 + 1) // 9007199254740992
console.log(9007199254740991 + 2) // 9007199254740992 ❌ 精度丢失
如果你要处理 超过 Number 最大安全值的整数,有几种常见方案👇
方案1:BigInt
现代 JavaScript 提供了一种新的类型:BigInt。
它可以表示任意大的整数。
const num = BigInt("9007199254740993")
console.log(num + 1n)
// 9007199254740994n
用 BigInt 的话,数字后面会跟着一个字母 n,看到它就能区分这个值和普通的 Number 类型不一样,这个需求可能会涉及很大的数值。
它还有一种简写方法,在赋值的时候不需要加引号括者数字,而是在数字后面加个 n。
const num = 9007199254740993n
需要注意的是,BigInt 不能和 Number 混合运算!!!
1n + 1
// ❌ 报错
必须统一类型:
1n + BigInt(1)
金融计算为什么不能直接用 Number
一个经典问题:
0.1 + 0.2
# 结果是 0.30000000000000004
原因是浮点数精度问题。
金融系统一般有两种解决方案。
方案一:金额用“分”存储
例如:
123.45 元
存储为:12345 分
前端展示时再除以 100。
const amount = 12345 / 100
console.log(amount)
// 123.45
这种方式是比较老派的方法。
方案二:使用大数库
**MikeMcl 写了几个很出名的处理数字的JS库,比如:
- bignumber.js:github.com/MikeMcl/big…
- decimal.js:github.com/MikeMcl/dec…
- big.js:github.com/MikeMcl/big…
我用 bignumber.js 演示一下。
安装 bignumber.js
npm install bignumber.js
在前端项目里引入:
import BigNumber from 'bignumber.js';
此时直接计算小数位的数值
const a = new BigNumber(0.1)
const b = new BigNumber(0.2)
a.plus(b).toString()
// 0.3
// 格式化,保留2位小数
a.plus(b).toFormat(2)
// 0.30
处理数值比较大的数据也没问题
// 1. 创建大数(建议始终传入字符串)
const x = new BigNumber('9007199254740995.123456789');
const y = new BigNumber('100');
// 2. 加减乘除
const res = x.plus(y); // 加
const res2 = x.minus(y); // 减
const res3 = x.times(y); // 乘
const res4 = x.div(y); // 除
console.log(res.toString()); // "9007199254741095.123456789" (精度完全保留)
格式化数值
在金融行业,金额的展示不仅关乎美观,更关乎准确性和合规性。针对你提出的千分位转换、中文大写转换以及大数处理
千分位格式化
方案1:toLocaleString()
JavaScript 原生支持国际化格式化。
const amount = 123456789.56
amount.toLocaleString()
// 123,456,789.56
方案2:Intl.NumberFormat
金融项目比较推荐使用这个方案。
const formatter = new Intl.NumberFormat('zh-CN', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})
console.log(formatter.format(1234567.8))
// 1,234,567.80
如果想在金额前面加一个“钱”的符号,比如人民币就加个 ¥,可以这么写:
const formatter = new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
})
console.log(formatter.format(123456))
// ¥123,456.00
如果要使用美元符 $ 就这么写:
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
})
console.log(formatter.format(123456))
// $123,456.00
方案3:正则实现千分位(不推荐)
function formatNumber(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}
console.log(formatNumber(123456789))
// 123,456,789
如果需要支持小数就这么写:
function formatMoney(num) {
return num.toString().replace(/\d+/, function(n) {
return n.replace(/(\d)(?=(\d{3})+$)/g, '$1,')
})
}
但金融系统一般不推荐这种方式,因为这种方式不支持国际化,不支持货币格式,容易出 bug。
数字转大写中文
在中文的金融系统中,经常需要展示:
123456.78
↓
壹拾贰万叁仟肆佰伍拾陆元柒角捌分
这个规则有点复杂,一般不建议自己实现。
我推荐使用开源库 nzh(github.com/cnwhy/nzh)。
安装:
npm install nzh
使用:
import nzh from "nzh"
nzh.cn.encodeS(123456)
// 十二万三千四百五十六
nzh.cn.encodeB(123456.78)
// 壹拾贰万叁仟肆佰伍拾陆点柒捌
nzh.cn.toMoney(123456.78)
// 人民币壹拾贰万叁仟肆佰伍拾陆元柒角捌分
最后总结一下。
在 JavaScript 中处理金额和大数时,核心要记住三点:
1️⃣ 不要直接用 Number 做金融计算 2️⃣ 金额存储最好使用整数(分) 3️⃣ 展示时再进行格式化
只要遵循这三条原则,就可以避免绝大多数金额相关的 bug。
以上就是本文的全部内容了,还有疑问的话可以在评论区交流。
点赞 + 关注 + 收藏 = 学会了