本文深入解析JavaScript中实现数字千分位格式化的六种常见方法,涵盖数组操作、字符串拼接、正则匹配及截取等技巧。通过对比不同数值下的执行耗时,分析各方案在循环次数、正则消耗及内存使用上的差异,最终推荐综合性能最优的slice截取方案,帮助开发者在代码简洁性与运行效率间做出最佳选择。
JavaScript实现数字千分位格式化的6种方案与性能对比
数字千分位格式是指从个位数开始,每三位数字之间插入一个逗号,例如“10,000”。针对这一需求,最初编写的函数实现如下:
方法一:数组操作法
代码如下:
// 方法一
function toThousands(num) {
var result = [ ], counter = 0;
num = (num || 0).toString().split('');
for (var i = num.length - 1; i >= 0; i--) {
counter++;
result.unshift(num[i]);
if (!(counter % 3) && i != 0) { result.unshift(','); }
}
return result.join('');
}方法一的执行逻辑是将数字转换为字符串后拆分为数组,随后从末尾开始逐个将元素插入到新数组(result)的头部。每插入一个元素,计数器counter加1,当counter为3的倍数且非首位(i不为0)时插入逗号。最后通过join方法合并数组得到结果。
该方法逻辑清晰,曾在项目中应用一段时间。但直觉表明其性能表现不佳。
方法二:字符串拼接法
方法二——方法一的字符串版
代码如下:
// 方法二
function toThousands(num) {
var result = '', counter = 0;
num = (num || 0).toString();
for (var i = num.length - 1; i >= 0; i--) {
counter++;
result = num.charAt(i) + result;
if (!(counter % 3) && i != 0) { result = ',' + result; }
}
return result;
}方法二是对方法一的优化,不再将字符串拆分为数组,而是全程对字符串进行操作。
方法三:正则匹配法
方法三——循环匹配末尾的三个数字
代码如下:
// 方法三
function toThousands(num) {
var num = (num || 0).toString(), re = /\d{3}$/, result = '';
while ( re.test(num) ) {
result = RegExp.lastMatch + result;
if (num !== RegExp.lastMatch) {
result = ',' + result;
num = RegExp.leftContext;
} else {
num = '';
break;
}
}
if (num) { result = num + result; }
return result;
}
方法三采用完全不同的算法,通过正则表达式循环匹配末尾的三个数字。每次匹配成功后,将匹配内容和逗号插入结果字符串头部,并将num更新为剩余未匹配部分(RegExp.leftContext)。需注意以下两点:
1. 若数字位数为3的倍数,最后一次匹配的是三个数字,但最前面的三个数字前无需加逗号; 2. 若数字位数不是3的倍数,循环结束后num变量会剩余1到2个数字,需将剩余数字插入结果字符串头部。
虽然方法三减少了循环次数(每次处理三个字符),但由于使用了正则表达式,一定程度上增加了额外消耗。
方法四:字符串截取法
方法四——方法三的字符串版
代码如下:
// 方法四
function toThousands(num) {
var num = (num || 0).toString(), result = '';
while (num.length > 3) {
result = ',' + num.slice(-3) + result;
num = num.slice(0, num.length - 3);
}
if (num) { result = num + result; }
return result;
}
事实上,截取末尾三个字符的功能可以通过字符串类型的slice、substr或substring方法实现,从而避免使用正则表达式。
方法五:分组合并法
方法五——分组合并法
代码如下:
// 方法五
function toThousands(num) {
var num = (num || 0).toString(), temp = num.length % 3;
switch (temp) {
case 1:
num = '00' + num;
break;
case 2:
num = '0' + num;
break;
}
return num.match(/\d{3}/g).join(',').replace(/^0+/, '');
}
该方法先将数字位数补足为3的倍数,利用正则表达式将其切割为每三个数字一组,再通过join方法添加逗号,最后移除补位的0。
方法六:正则替换法
方法六——懒人法
代码如下:
// 方法六
function toThousands(num) {
return (num || 0).toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
}一直认为格式化可以通过一条正则表达式替换完成,这需要用到断言等写法。经Google搜索,确实找到这条正则表达式,这可能是代码最短的实现方式。
性能测试结果与分析
测试结果
| 数字 | 执行5000次消耗的时间(ms) | |||||
|---|---|---|---|---|---|---|
| 方法一 | 方法二 | 方法三 | 方法四 | 方法五 | 方法六 | |
| 1 | 4 | 1 | 3 | 1 | 14 | 2 |
| 10 | 14 | 1 | 3 | 0 | 7 | 2 |
| 100 | 12 | 1 | 2 | 4 | 5 | 3 |
| 1000 | 13 | 2 | 3 | 2 | 9 | 5 |
| 10000 | 21 | 4 | 3 | 1 | 6 | 3 |
| 100000 | 21 | 3 | 2 | 1 | 5 | 6 |
方法一和方法二的强烈对比表明,字符串操作的效率远高于数组操作;方法六的测试结果说明代码长短与性能高低无直接关系。方法四的综合性能最佳(尽管num为100时性能略有下降,原因尚不明确),主要原因在于:
1. 对比方法一、二,每次操作3个字符而非1个,减少了循环次数; 2. 对比方法三、五、六,未使用正则表达式,减少了额外消耗。
最终选择方法四作为优化方案。如有更好的实现方法或建议,欢迎评论交流。