前言
最近读了一本书,张容铭老师的 《JavaScript设计模式》 ,书中提到的编码模式,编码风格,让我想起了我刚学 JS 的时候,特别向往像大神一样,全篇没什么 div 罗列,都是方法或者构造函数之类的东西,一顿操作后, body 里面只要添加一个 <div id=“demo”> ,大功告成。
逼格满满有没有,当时就想,什么时候我也能这样,别人看不懂我的代码,所有的事情不用自己手写,只要给一个容器,然后执行函数就行了。
后来工作过程中,大部分项目的开发时间都比较紧迫,为了快速满足当前的项目需求,没做什么可维护性的研究,能直接加就直接加,添加元素太多的话,就创建个数组遍历,这就导致了重复代码非常多,可维护性相当差。
再后来封装了一些小组件,引用了一些 UI 库,比如 AntD 倒是解决了一小点,代码重复的问题。但是心里的那股向往,一直没有熄灭。想写出大神代码,苦于没有个系统的方法。直到我搜到了这本书,简直就像开启了人生第二春。每一页的字里行间,都能与我的思维碰撞出火花,短短几天,我已经觉得像蜕变一样了。曾经的我和现在我,已经不是一个维度的了。
当然了,不敲代码,永远都是纸上谈兵,个人还是需要一段时间的沉淀,真正融会贯通,把学到的用出来,才算的上是学到了。
前期准备
各位乡绅,读这本书前,个人建议要对 继承 , 类 (虽然JS在ES6之前没有类,但是可以通过一些手段实现类), 构造函数 , 原型链 ,这些概念要有一个清晰的认识,这个认识需要你能明确的知道这些东西是什么,有什么用。
举个例子,当遇到 prototype 这个单词的时候,要立马能想到,这个是原型,每一个构造函数在创建时就生成一个原型,通过该构造函数的创建的实例能从此原型上继承构造函数的属性和方法,同时构造函数又是继承了函数原型的属性和方法,而函数又是继承了对象原型。
看到上面这些东西,初学者可能会有点懵,很绕,我帮各位乡绅整理了一下上面的知识点,可以移步下面三篇文章。
prototype,proto,constructor到底是什么关系(图解)
JavaScript各种继承,原型继承,构造函数继承,组合继承,寄生组合,ES6继承,我能学会你也可以
JavaScript中的this指向,面试官再问this就秀他一脸
如何才能让别人看不懂
这里的看不懂是说代码要写的专业,优美,不是说写乱一点,让别人找不到那种哈。
1.四两拨千斤
个人在初期编码的时候,没怎么注意过内存消耗,一般有需要了,直接来个变量,存一下,先实现功能再说。但是这个功能实现后,想回头优化,感觉又无从下手,密密麻麻一大堆,哪个变量都不能删。
久而久之,内存溢出,导致页面会在你不知道的情况下,卡住,或者白屏。修改定位起来又异常困难,所以平时编码一定要注意内存控制,这方面就得提一下继承。
几乎每种语言都有继承,为什么继承这么重要?因为它可以 节约内存 。
举个例子,我把一个数组去重的方法,写在一个构造函数里,那么每一个通过该构造函数创建的实例都会有这个去重方法,如果每个实例都单独存一份这个方法的话,那内存占用是相当恐怖的,同时,当不调用这个方法的时候,这块内存就被这个方法占用着,别人用不了不说,他自己也不用,就占着**不拉*。
所以 JS 非常巧妙的,只把那个去重方法存一份,当实例需要使用的时候,通过原型链去寻找那个方法,然后使用它。这样一个方法只存了一份,但是任何通过构造函数创建的实例都能使用。
所以要想让别人看不懂,就得用最少的内存,做最多的事。
2.指桑骂槐
当遇到一个问题的时候,不是直接去解决它,而是利用某种特性,侧面达到解决问题的效果。还拿数组去重举例子。
首先遇到这个问题的时候,我们最想想到的就是嵌套两层循环,如果第一次循环与第二次循环有值相等,那么就删掉第二个循环中的值
function distinct(arr) {
for (let i=0, len=arr.length; i<len; i++) {
for (let j=i+1; j<len; j++) {
if (arr[i] == arr[j]) {
arr.splice(j, 1);
// splice 会改变数组长度,所以要将数组长度 len 和下标 j 减一
len--;
j--;
}
}
}
return arr
}
这个方法很容易想到,但是仔细琢磨,效率好像有点低,而且直接正向去解决这个问题,有点硬碰硬的意思。我们需要换个思路。
数组去重无非就是要求每个元素独一无二罢了,那么对象有一个特性,那就是各个属性唯一,正好符合我们现在的需求,我们就可以按照下面的方法。
function distinct(arr) {
let result = []
let obj = {}
for (let i of arr) {
if (!obj[i]) {
result.push(i)
obj[i] = 1
}
}
return result
}
这么一搞是不是有很多同学开始看不懂了,别急,我们解释一下,上面代码中遍历数组,一开始有一个空对象,如果这个空对象没有数组元素命名的属性,那就把这个元素添加到去重后的数组里,然后给刚才的对象新增一个属性,随便给个值(示例中给的1)就行了。因为对象属性唯一,所以当出现重复的数据时,就不会进入到条件判断里,那么最终输出的结果,也就达到了去重目的。
各位这会儿是不是感觉有点意思了,没完哈,想要让别人看不懂,还不够。要彻底的让对方折服,就得在效率上找找文章,上面两个去重方法,在处理 15万 长度的数据时,第一个方法需要 1万4千多毫秒 ,第二个方法只要 9毫秒 。
所以当有人问你为什么用这个方法的时候,一定要把逼装全了,告诉他嵌套循环的那种方法,有性能问题~。深藏功与名。
先写这么多,后面我会把这个主题整理成一个系列来与各位交流。都写到一篇的话,接受起来会比较累(不是因为我懒~)嘻嘻~