美文网首页
JS前置补零的方法和性能测试

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

作者: 囍冯总囍 | 来源:发表于2020-08-06 16:39 被阅读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++;
    }
    

    相关文章

      网友评论

          本文标题:JS前置补零的方法和性能测试

          本文链接:https://www.haomeiwen.com/subject/ebgzrktx.html