做报表的时候偶尔会遇到这种需求:
为了补齐长度,在一个数字字符串前面添加 N 个 0 占位
举个例子:
单元格需要展示 6 位数字,如 ‘123456’
但后端返回的数字是 123,这时候就要在前面补 0,得到 ‘000123’,使其长度为 6
那就需要实现一个方法,基于 number 类型的参数 n,返回由 n 个 0 组成的字符串
以及,在众多方案中,哪一个方案的耗时最短?
测试模板:
// pad_start.js
function getPlaceholder(n, mark = '0') {
// ...
}
function padStart(num, len) {
const length = len - String(num).length;
return length > 0 ? `${getPlaceholder(length)}${num}` : `${num}`;
}
console.time("Running");
const str = padStart(123, 10000000);
console.log('字符长度:', str.length.toLocaleString());
console.timeEnd("Running");
在模板代码中,需要实现 getPlaceholder 方法来添零占位,然后统计补全千万长度的数字需要多少时间
方案一:for 循环
function getPlaceholder(n, mark = '0') {
let str = '';
for (let i = 0; i < n; i++) {
str += mark;
}
return str;
}
作为兜底方案,for 循环可能会迟到,但从不缺席…
方案二:Math.pow
function getPlaceholder(n) {
return Math.pow(10, n).toString().slice(1);
}
由于需求是添“0”占位,可以取个巧,通过 Math.pow() 生成 10 的倍数,然后删掉首位的“1”,就得到了一串“0”
不过如果数值过大,JS 会使用科学计数法,所以这里最健壮的写法应该是:
function getPlaceholder(n) {
return Math.pow(10, n).toLocaleString('zh-CN').replace(/,/g, '').slice(1);
}
即便如此,由于 JS 存在数值上限 Number.MAX_VALUE,所以 Math.pow(10, 309) 的结果为 Infinity,当字符长度超过 308 时就无法使用该方案
这种方案的局限性太大,除了上面说的情况以外,如果需要使用“0”以外的占位符就用不了
所以就不对该方案测速了…
方案三:Number.toFixed
function getPlaceholder(n) {
return (1).toFixed(n).replace('.', '').slice(1);
}
如果对一个整数调用 toFixed,小数点后会用 0 来补位。基于这个特性可以很方便的生成一串“0”
和方案二类似,这种方案只适用于用“0”占位的场景
而且 toFixed 的入参只能是 0 ~ 100 的数字,无法处理超长的字符串,所以也不测速了…
方案四:Array.fill
function getPlaceholder(n, mark = '0') {
return Array(n).fill('0').join('');
}
生成一个长度为 n 的数组,然后将元素都设为“0”,最后通过 join 拼成字符串
比较全面的方案,相对简洁,也可以自定义占位符
除了 Array.fill 之外,还可以使用其他方法,如 Array.from、Array.map
这种方案的速度就快多了,比 for 循环节省了近六成的时间
方案五:Array.join
function getPlaceholder(n, mark = '0') {
return Array(n + 1).join(mark);
}
方案四的进化版,new Array 之后会生成由 empty 构成的数组,可以直接对该数组做 join
有一点需要注意,生成的数组长度得是 n + 1,因为 join 是对数组元素的间隙做填充
相比于方案四,少了一次 Array.fill(),所以速度明显提升
方案六:String.padStart
ES2017 新增的方法,可以直接完成字符串的填充,参考 MDN > padStart
const num = 123;
// 下面的 8 是总长度
String(num).padStart(8, "0");
// "00000123"
类似的还有 padEnd
对该方案的测试,需要稍微修改下测试模板的代码:
console.time("Running");
const str = String(123).padStart(10000000, "0");
console.log('字符长度:', str.length.toLocaleString());
console.timeEnd("Running");
这速度离谱吗?确实离谱!但这不是最离谱的…
我把字符长度从一千万逐步加到十亿,耗时居然没啥变化 ?!
可惜我翻遍了各种资料,也没查到如此高效的 String.padStart 是如何实现的…
如果有小伙伴知道原因,请一定给我留言,或者来一发传送门,拜谢~