## 目录
- [前言](#%E5%89%8D%E8%A8%80)
- [需求描述](#%E9%9C%80%E6%B1%82%E6%8F%8F%E8%BF%B0)
- [解决方法](#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95)
- [方法一:修正字符串处理方法](#%E6%96%B9%E6%B3%95%E4%B8%80%E4%BF%AE%E6%AD%A3%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%A4%84%E7%90%86%E6%96%B9%E6%B3%95)
- [方法二:正则表达式处理](#%E6%96%B9%E6%B3%95%E4%BA%8C%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E5%A4%84%E7%90%86)
- [方法三:使用 Number 方法](#%E6%96%B9%E6%B3%95%E4%B8%89%E4%BD%BF%E7%94%A8-Number-%E6%96%B9%E6%B3%95)
- [总结](#%E6%80%BB%E7%BB%93)

## 前言
你好,我是喵喵侠。在数据可视化中,我们经常会遇到需要对数据进行格式化的需求。例如,在 Echarts 的环形图中,我们希望在图表中心的总数,显示经过计算后的结果,比方说25.66这样的小数,默认保留两位小数。但光保留两位小数可不行,还得去掉多余的末尾0。下面来一起探究如何优雅实现吧!
## 需求描述
假设现在有这样一个饼图,中间需要显示数字:

这个总数是通过每个扇区计算出来的,计算结果要求保留两位小数。保留两位小数很简单,总数计算后,调用下`toFixed(2)`就好了。但是这样会有个问题,如果结果是上面这样的25.6,保留两位小数后就是25.60,这个后面的0是不需要的。同理,如果计算结果是25.00,这个后面的00也是不需要的。
确切的说,当总数的小数位数超过2位的时候,只保留2位小数;如果得到的结果小数位末尾有多余的0,则需要去掉。
## 解决方法
针对这个数字的处理,我有三种解决方法,一起来看看吧。
### 方法一:修正字符串处理方法
这个方法通过将数字转换为字符串并手动处理小数部分来达到预期效果:
```javascript
function formatNumber(num) {
let str = num.toFixed(2);
if (str.indexOf(".") > -1) {
let parts = str.split(".");
let decimalPart = parts[1];
// 去掉末尾的0
while (decimalPart.endsWith("0")) {
decimalPart = decimalPart.slice(0, -1);
}
// 如果小数部分为空,则仅返回整数部分
str = decimalPart ? `${parts[0]}.${decimalPart}` : parts[0];
}
return str;
}
let num1 = 12.5;
let num2 = 12.0;
let num3 = 12.34;
console.log(formatNumber(num1)); // 输出 "12.5"
console.log(formatNumber(num2)); // 输出 "12"
console.log(formatNumber(num3)); // 输出 "12.34"
```
这种方法的核心思路是,把数字转换成字符串,通过小数点.来切割成两个数组,索引为1的数组是小数部分,通过while循环判断,如果字符串的末尾有0,那么用slice方法截取字符串第0位到倒数第2位(也就是-1,end位置这个不包含)。直到末尾找不到0,则跳出循环。最后三元判断,判断小数部分是否还存在(存在小数部分都是0被截取丢弃完的情况),然后通过字符串拼接的方式组合出新的总数。
### 方法二:正则表达式处理
使用正则表达式也可以达到类似的效果,以下是一个简洁的实现:
```javascript
function formatNumber(num) {
return num.toFixed(2).replace(/\.?0+$/, '');
}
console.log(formatNumber(12.50)); // 输出 "12.5"
console.log(formatNumber(12.00)); // 输出 "12"
console.log(formatNumber(12.34)); // 输出 "12.34"
```
这种方法比较简单,一行代码就搞定。但理解起来需要对正则比较熟悉,尤其是这个`?`的含义要知道是什么,这个是点睛之笔。下面我解释下`/\.?0+$/`这个正则的含义:
- `/` 和 `/`:表示正则表达式的起始和结束。
- `.?`:匹配一个小数点 `.`,其中 `.` 用于转义小数点,因为小数点在正则表达式中是一个特殊字符。`?` 表示前面的小数点是可选的,也就是说这个部分匹配“零个或一个”小数点。
- `0+`:匹配一个或多个零 (`0`),`+` 表示前面的 `0` 可以出现一次或多次。
- `$`:表示字符串的结尾,确保匹配发生在字符串的末尾。
比方说数字是123.10,那么这个.?表示这里末尾0的左边可以没有小数点,匹配的就是`0`,`replace`后得到的是123.1。
如果数字是123.00,这种情况小数点和后面紧跟的0就都匹配上了,匹配的部分是`.00`,小数点和末尾的0就都去掉了,`replace`后的结果就是123。
### 方法三:使用 `Number` 方法
最简单的方法是直接用 `Number` 转换字符串,这样会自动去掉多余的零:
```javascript
function formatNumber(num) {
return Number(num.toFixed(2));
}
console.log(formatNumber(12.50)); // 输出 "12.5"
console.log(formatNumber(12.00)); // 输出 "12"
console.log(formatNumber(12.34)); // 输出 "12.34"
```
## 总结
对字符串数组操作比较熟练的同学,可能会写出方法一,但需要考虑的细节比较多,得反复测试;方法二也是需要反复测试验证,只是写法实现更简洁一些;最好的方法还是方法三,用内置的Number构造函数,直接就去掉了末尾多余的0,没有任何副作用,以后建议就用它吧!