实现思路
拿到这个问题的时候先从最简单的入手,假如我们拿到的是一个四位数的整数,那么问题就变得好简单了。
首先要确认的一点是通过使用replace方法与平常有所不同
replace正常情况下是对字符串从前往后匹配
现在我们需要的是从后往前匹配,‘各十百千’是从后往前数的
于是乎就有了一下函数方法
技术方法
- 正则对象的 test() 方法
- 字符串的 replace() 方法
代码实现
function thousands(num) {
// 转成 Number 类型
num = +num
// 判断是否是一个合法数
if (isNaN(num)) {
return '请传入一个合法数'
}
num = '' + num
let [intNum, floatNum] = num.split('.')
// 手动巧妙添加一个','标识符,方便循环匹配
intNum += ','
while (/\d{4}/.test(intNum)) {
intNum = intNum.replace(/(\d{1})(\d{3}\,)/, '$1,$2')
}
// 删除我们刚刚添加的多余的 ','
intNum = intNum.slice(0, -1)
// 整数部分与小数部分拼接起来返回(小数部分做了判空)
return intNum + (floatNum ? '.' + floatNum : '')
}
let s = '1234156567839.123'
console.log(thousands(s));
// 1,234,156,567,839.123
思考
既然循环使用replace实现,为什么不使用全局替换呢? 于是灵光乍现,就有了下面这种方法
function thousands(num) {
// 转成 Number 类型
num = +num
// 判断是否是一个合法数
if (isNaN(num)) {
return '请传入一个合法数'
}
num = '' + num
let [intNum, floatNum] = num.split('.')
// 整数部分前后反转
intNum = intNum.split('').reverse().join('')
// 现在就是从前往后替换千分位的‘,’了
intNum = intNum.replace(/\d{3}/g, '$&,')
// 最后再反转回去
intNum = intNum.split('').reverse().join('')
// 上面三个部分当然可以简写成下面的一行,前提是: 如果你不怕你的同事打你的话 哈哈哈...
// intNum = intNum.split('').reverse().join('').replace(/\d{3}/g, '$&,').split('').reverse().join('')
// 整数长度为是三的倍数情况 去除第一个‘,’
if (intNum[0] === ',' || intNum[1] === ',') {
intNum = intNum.replace(',', '')
}
// 最后 整数 + 小数 返回 搞定!
return intNum + (floatNum ? '.' + floatNum : '')
}
let s = '1234156567839.123'
console.log(thousands(s));
// 1,234,156,567,839.123
写在最后
最后,你可能比较好奇两种方式的执行效率上哪种更好呢? 比较了一下
- 比较方式时间维度
console.time()
console.log(thousands(s));
console.timeEnd()
- 前者
default: 5.549ms, 5.643ms,6.109ms
- 后者
default: 6.486ms, 6.157ms, 5.466ms
结论
两者的效率旗鼓相当,从代码量上面和理解的难易程度上来看。个人更倾向与后者。
就补充这么多了,(厚着脸皮说)转载跟我说一下哈!
不对的地方也请指教,多谢!
补充:正则实现
如果使用正则来实现,那么…
replace(/\B(?=(\d{3})+(?!\d))/g, ",")
搞定!