如何写好JavaScript肯定是每一个前端工程师一直以来要思考的问题,月影老师告诉我们一些写好JavaScript的原则,同时也教了一些我们如何写好JavaScript的技巧,今天来继续跟着月影老师学JavaScript吧~~
我们在写代码的时候,最应该关注的什么?
程序是写给人读的,只是偶尔让计算机执行一下。 —— Donald Ervin Knuth(高德纳)
编码风格与编码效率哪个才是写好代码最应该关注的呢?在大多数情况下,对效率没有那么极致的要求时,我们要优先编码风格,编码可读性,这样可以提高我们代码的可维护性。风格与效率,要根据使用场景来判断!
回顾一下2016年引起热议的Left-pad事件
图片来自大圣2016年的博客
事件本身有很多槽点
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进行快速幂操作
我们来解读一下这段代码。这里用到了位运算: &
与运算 与 >>>
无符号右移运算
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个 *
所以这里的时间复杂度是 O(logN) 循环的次数 是 count的二进制的位数
其实还是可以继续优化的
var rpt = ""; do { rpt += str; str += str; count &= count - 1; } while(count);
count &= count - 1;
可以去掉二进制中最后面的1
1100100
-> 1100000
-> 1000000
-> 0000000
这里循环的次数 是 count的二进制数中1的位数
比如100的二进制是 1100100
,就会循环 3 次
但是这种情况达不到我们要的结果,应该是老师PPT弄错了~或者是我理解错了,如果知道什么情况的,可以留言或者私信教教我
然而有意思的是,现在 MDN 给出的ployfill代码已经更改成使用while循环这样的代码了