JS前置补零的方法和性能测试

2020-08-06  本文已影响0人  囍冯总囍

今天遇到一个问题,需要将一组数字格式化为指定长度进行前置补零。于是突发奇想,到底哪种方法效率最高呢?今天我们就来测试一下,废话不多说,直接lie代码(这里我们还是用NodeJS内的perf_hooks库来实现性能监控)

起初的测试

这里按照常规套路,将重复执行的次数设置为了interval=1000000,并将原始数字设置为num=123,len=10,或想着通过多次的执行来放大延迟的效果,得到的结果如下:

// 引入performance包
const {
    performance,
    PerformanceObserver
} = require('perf_hooks')

// 新建一个Observer,并在面实现打印duration
const obs = new PerformanceObserver((list, observer) => {
    list.getEntries().forEach(i => console.log(`Performance ${i.name} duration:${i.duration}`))
    observer.disconnect()
});

// 配置这个Observer只接受measure和function,并开启结果缓存
obs.observe({
    entryTypes: ['measure', 'function'],
    buffered: true
});

// 华丽而不失优雅的分割线=========================================
// 下面开始网上几种比较常见的方法的性能测试
const interval = 1000000
// 这里我们取数字123,要求返回10位补零后的字符串
const num = 123,
    len = 10
let  tmp

performance.mark('1 S')
for (let i = 0; i < interval; i++) {
    tmp = num.toString()
    let j = tmp.length;
    while (j < len) {
        tmp = "0" + tmp;
        j++;
    }
}
performance.mark('1 E')
console.log('1', tmp)
performance.measure('1', '1 S', '1 E')


performance.mark('2 S')
for (let i = 0; i < interval; i++) {
    tmp = (Array(len).join('0') + num).slice(-len)
}
performance.mark('2 E')
console.log('2', tmp)
performance.measure('2', '2 S', '2 E')


performance.mark('3 S')
for (let i = 0; i < interval; i++) {
    tmp = (num / Math.pow(10, len)).toFixed(len).substr(2)
}
performance.mark('3 E')
console.log('3', tmp)
performance.measure('3', '3 S', '3 E')


performance.mark('4 S')
for (let i = 0; i < interval; i++) {
    tmp = ("00000000000000000000000" + num).substr(-len);
}
performance.mark('4 E')
console.log('4', tmp)
performance.measure('4', '4 S', '4 E')

看下结果吧:

1 0000000123
2 0000000123
3 0000000123
4 0000000123
Performance 1 duration:124.4382
Performance 2 duration:609.8136
Performance 3 duration:350.801199
Performance 4 duration:121.3448

经过多次测试,虽然每次时间都不一样,但是结果还是大致相同的。与网上那么多教程中说的恰恰相反,速度最快的是1和4。网上很多文章转载的结果基本都是说第二种方法是最快的???这是什么情况呢???到底哪里出了问题?

其实小伙伴们也许已经发现,方法2和方法4其实原理几乎一样,只不过方法2更加灵活,不受len的限制,但是这样牺牲的却是运行效率(Array(len).join('0')的效率比较低)。如果硬要用这种更为灵活的方法,还不如选择方法1或方法3

减少重复次数后测试

既然有了performance神器,我们将循环去掉,看看每个方法的单次执行结果如何呢:

// interval = 0 直接将for循环注释掉了得到的结果
1 0000000123
2 0000000123
3 0000000123
4 0000000123
Performance 1 duration:0.0905
Performance 2 duration:0.3641
Performance 3 duration:0.1794
Performance 4 duration:0.007101

多次测试后结果基本一致,那就是方法4效率最高!这个说得通啊,方法四只是一个字符串拼接和取substr的过程,在一定长度下没理由比方法1慢啊!方法2依然最慢。那么我们发现,只执行一次的时候方法四最快。

接下来的测试我们都把for循环去掉,就用单次执行的时间来对比。

两位数测试

这里再来测试比较常见的场景,如2位数字的格式化(日期啊时间中常用),设置上面的num=2,len=2后,看看哪个方法的效率更高呢:

1 02
2 02
3 02
4 02
Performance 1 duration:0.0955
Performance 2 duration:0.5985
Performance 3 duration:0.2262
Performance 4 duration:0.010299

多次测试是方法4完胜。那如果len=num.length的情况下呢?

// len=2,num=12
1 12
2 12
3 12
4 12
Performance 1 duration:0.2626
Performance 2 duration:0.5767
Performance 3 duration:0.455599
Performance 4 duration:0.0163

依旧是方法4。

异常测试

一、num<len的情况

这种情况下,不同的算法得到的结果是不同的,没有对错,只能说看你需要,直接上结果:num=1234,len=2

1 1234
2 34
3 .34
4 34
Performance 1 duration:0.0865
Performance 2 duration:0.6398
Performance 3 duration:0.238099
Performance 4 duration:0.0074

看到了咩!!!出现了BUG!方法3中出现了一个小数点。原因是因为这个方法之前用的substr(2),也就是取第二个字符后的字符串(作者可能想当然认为不存在小数点前有超过1位的情况了),所以这里我们改成substr(-len)更为合适。

二、超长位数测试

其实这个测试的意义不大,因为每个方法,根据它们的实现原理来说都有各自的限制,比如方法4,如果长度超出了前面预设的000000000的长度,则不能返回正确的结果。所以在常用的场景下,方法四是最好的选择。

三、num为小数的测试

这里设置num=12.345,len=10,得到的结果如下:

1 0000012.34
2 0000012.34
3 0000000012
4 0000012.34
Performance 1 duration:0.1214
Performance 2 duration:0.364799
Performance 3 duration:0.184201
Performance 4 duration:0.007899

方法3由于实现原理上的问题导致数据异常,接下来再测试一下num=0.123,len=2的场景:

1 0.1234
2 34
3 00
4 34
Performance 1 duration:0.101199
Performance 2 duration:0.5365
Performance 3 duration:0.195501
Performance 4 duration:0.008401

可以看到,根据2和4的实现原理,只取了最后两位字符。方法一没有在小数点前补0,方法3经过运算后在0的前面补了2个0

方法总结

对于可控长度范围下的前置补零或返回原数据,则通过字符串运算的方式效率最高:

num.length>len?num:('00000000000000'+num).substr(-len)

对于最长需要补多少个0不太确定的场景,可以通过方法1来实现:

tmp = num.toString()
let j = tmp.length;
while (j < len) {
    tmp = "0" + tmp;
    j++;
}
上一篇下一篇

猜你喜欢

热点阅读