阅读 456
【青训营】月影老师告诉我写好JavaScript的四大技巧——风格优先

【青训营】月影老师告诉我写好JavaScript的四大技巧——风格优先

如何写好JavaScript肯定是每一个前端工程师一直以来要思考的问题,月影老师告诉我们一些写好JavaScript的原则,同时也教了一些我们如何写好JavaScript的技巧,今天来继续跟着月影老师学JavaScript吧~~

我们在写代码的时候,最应该关注的什么?

image.png

程序是写给人读的,只是偶尔让计算机执行一下。 —— Donald Ervin Knuth(高德纳)

编码风格与编码效率哪个才是写好代码最应该关注的呢?在大多数情况下,对效率没有那么极致的要求时,我们要优先编码风格,编码可读性,这样可以提高我们代码的可维护性。风格与效率,要根据使用场景来判断!

Left-pad事件

回顾一下2016年引起热议的Left-pad事件

图片来自大圣的2016年的博客.png 图片来自大圣2016年的博客

事件本身有很多槽点

  • NPM 模块粒度
  • 代码风格
  • 代码质量/效率
function leftpad(str, len, ch){
    str = String(str);
    
    var i = -1;
    
    if(!ch && ch !== 0) ch = "";
    
    len = len - str.length;
    
    while(++i < len){
        str = ch + str;
    }
    
    return str;
}
复制代码

月影老师说这里这个代码他觉得问题不大,这是一段好懂的代码。

首先谈到NPM模块粒度的问题,为什么一个函数就构成了一个模块,粒度是不是太细了,其实当年的模块化还没有那么完善,tree shaking功能不太行,所以粒度不得不弄这么小。现在来说是粒度太细了,但是在当年来说是无可厚非的。

代码风格问题,这个代码写的可读性很高,很简单的代码。好的代码本身即注释

最后是代码效率,这里确实效率不高,是O(N)的时间复杂度,但是结合实际,我们实际用的时候也不会用到前面拼很多很多的字符的情况,所以这样也是可以的。

综上所述,这段代码没啥问题~

我们来看看为何那么多人吐槽它的效率低,怎么改进能提升这段代码的效率呢?

我们就看循环部分,让你重复一个字符串n次,真的需要n次循环来一步一步的拼串吗?

while(++i < len){
    str = ch + str;
}
复制代码

比如要将 * 重复 100 遍

100 的 二进制是 1100100

while循环相当于是用十进制的思想,连续拼加了100次的*(感觉这里应该说是数数的思想)

而使用二进制的思想,每次*都翻倍(2的幂),只需要循环7次就可以完成,我们看下面的代码来理解

这是优化的代码

function leftpad(str, len, ch=""){
    str = "" + str;

    const padLen = len - str.length;
    
    if(padLen <= 0){
        return str;
    }else{
        return ("" + ch).repeat(padLen) + str;
    }
}
复制代码

这里用到了ES6的一些特性,默认赋值,字符串的repeat方法,重点是它怎么实现重复n次操作的

它在MDN上给出的ployfill实现中关键代码是这样的定义的

var rpt = ""
for(;;){
    if((count & 1) == 1) {
        rpt += str;
    }
    
    count >>>= 1;
    
    if(count == 0){
        break;
    }
    
    str += str;
}
复制代码

关于快速幂的算法解析,可以看我的博文 【算法】浅析使用JavaScript进行快速幂操作

image.png

我们来解读一下这段代码。这里用到了位运算: &与运算 与 >>>无符号右移运算

count & 1 取count二进制的最低位,判断和1是否相同,相同返回1,否则返回0

count >>> 1 把count的二进制右移一位,即去掉其二进制位的最低位

所以这段代码的意思就是逆序遍历 count 的二进制形式,str每次都翻倍,遇到二进制位是1的就将str拼到rpt中去

还是上面说的重复100次 * 的例子 倒着遍历100的二进制1100100,每次都对str进行翻倍操作,二进制中遇到1的时候就让rpt加上当前的str

这里会循环7+1次, str 分别是 1*(0)、 2*(0)、4*(1)、8*(0)、16*(0)、32*(1)、64*(1),所以最后rpt就是 4+32+64=100个 *

image.png

所以这里的时间复杂度是 O(logN) 循环的次数 是 count的二进制的位数

image.png

其实还是可以继续优化的

var rpt = "";
do {
    rpt += str;
    str += str;
    count &= count - 1;
} while(count);
复制代码

image.png

count &= count - 1; 可以去掉二进制中最后面的1

1100100 -> 1100000 -> 1000000 -> 0000000

这里循环的次数 是 count的二进制数中1的位数 比如100的二进制是 1100100,就会循环 3 次

但是这种情况达不到我们要的结果,应该是老师PPT弄错了~或者是我理解错了,如果知道什么情况的,可以留言或者私信教教我

image.png

然而有意思的是,现在 MDN 给出的ployfill代码已经更改成使用while循环这样的代码了😂😂😂

while (count) {
   str += str;
   count--;
}
复制代码

搞了半天效率又回去了~~ 哈哈

也就是说在大部分场景下,对效率的要求没有那么恐怖,所以注重代码风格优先仍然是写好JavaScript代码的一个重要技巧!

判断单位矩阵

我们看下面这段代码

image.png

这段代码对我看来,是有很多问题的

  1. 太长了,可读性一般
  2. 拓展性不高
  3. 封装性不好

但是,这是一段真实的代码

image.png

因为这是当前条件下,效率最高的方式,而这个场景是需要效率优先的。

所以说在实际开发中,我们还是要视具体情况而定的。

总结

这里我们学到了使用位运算实行快速幂,来减少循环次数,来提高我们的代码效率

但是实际上,在效率需求不是那么恐怖的情况下,我们更应该关注我们代码的可读性

最后,风格与效率是需要根据实际场合进行取舍的

更多相关博文

【青训营】月影老师告诉我写好JavaScript的三大原则——各司其责

【青训营】月影老师告诉我写好JavaScript的三大原则——组件封装

【青训营】月影老师告诉我写好JavaScript的三大原则——过程抽象

【青训营】月影老师告诉我写好JavaScript的四大技巧——风格优先

【青训营】月影老师告诉我写好JavaScript的四大技巧——保证正确

文章分类
阅读
文章标签