其实说起来不让写工作总结,但是实际上这还是一篇工作总结。。。。
ok!切入正题,我们来讨论一下如何简化代码,其实这也都是最近在实际开发时的体会,而且本身我不是很擅长这一点。但是最近因为写的代码逻辑略微繁复,而且本身需要兼容的情况比较多,加上繁复的现实条件(比如编译环境,比如设备性能,比如运行平台等等)的干扰,导致很多时候展示出来效果不是很好,所以就在本来逻辑较为复杂的代码上又添加了许多判断状态的代码,这就让代码的可读性,可维护性,健壮性都下降了很多,代码的耦合度直线上升,当具体的逻辑和实际的需求有任何一点点的变化,就会产生多米诺骨牌效果,不但改的时候很难理清添加了多层嵌套与判断的代码,在修改之后还可能牵连另外的功能产生意想不到的变化。所以,为了不让同志们屌我,我还是对代码进行了一定程度上的改造。
其实感觉上面还是在说废话。。。。当然啦,简单阐述一下博客的灵感来源嘛!~
其实很久以前看过类似的文章,专门介绍了很多提高代码可读性以及运行速度的方法,具体的文章题目与链接我忘记了,应该可以搜索“如何简化代码”找到。文章中的主要思想大致就是分离,合并:将该分离的一些变量,不需要强行的使用一个变量将内容统一,该添加变量的时候就添加新的变量;而合并就是将能统一判断的内容提升到高层,而不需要逐个写在每一个判断体或者循环体中,这样既能简化函数,又可以提升运行速度。
我也是以这样的思想为核心进行重构代码的,结合实际情况,主要有以下几点想法吧:
1. if判断体不要写的太长。if写多长其实本身并不影响逻辑,但是却很影响可读性。if本身是添加嵌套的,当写的很长了之后,会出现很现实的问题--------找不到else if与else的对应。当在if内部再嵌套了if之后,写的时候或许还比较清楚,但是当写完之后需要读或者改的时候,在看到一长串的if之后突然出来一个else,当然会产生这个else究竟对应谁的疑问吧!
2. 不要怕繁琐,多声明函数。上面说了,if不能写太长,那么如果if的逻辑真的很多那么该怎么办呢?
首先,你应该确定,if的逻辑真的很多,很多的逻辑其实是可以提升到判断体之外去做的,比如在不同的判断内容后,都需要将某一个计数器+1,那么这个计数器当然就可以放在判断的外面整体去做,而不是放在内部一个一个去做。这一步检查需要仔细一些,因为很多内容可能在编码的过程中写在了判断体的中间部位,而实际上有一些声明或者值的改变完全是可以放在开头和结尾的,这些都可以从判断体中提出。
其次就是要抽离函数了,这一步最简单粗暴的方法就是将函数体的内容复制粘贴,放到声明的另一个额外的函数之中。这一步在初学者看来可能有一些多此一举,但是在实际开发过程中,基于工程化,模块化的要求,这样写代码可以很直观的了解到哪一部分功能是什么,也提升了代码的可读性,在修改代码的时候也可以很明确的根据bug的表现定位到具体相关的位置。
最后一步,我们要再次确定,这些冗长的逻辑真的是必须的。这一部分是什么意思呢?因为在两个不同的判断体之中,我们往往判断的只是一个或者两个不同的条件,而需要调整的变量,对象,属性等等一般都是同一种或者同一类,在整理逻辑之后我们经常可以发现,调整的内容完全可以通过传入一些变量进行改变,这样就可以把一些类似的逻辑又抽成一个额外的函数,达到了简化代码的目的。
3.既然说到了函数那么就想聊聊回调函数了。为什么想聊一聊这个内容呢?因为这周自己在工作过程中写到了一个回调函数,虽然的确很难懂,但是这种函数式的操作真的很cooool~,而且功能极其强大。
首先,我们需要简单了解一下回调函数是什么。
百度百科:回调函数就是一个通过函数指针调用的函数。 如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。 回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
很多语言都有回调函数,其在语言的底层实现的方式虽然不同,但是其功能都是类似的,而且从方式上来看,都是将函数作为参数传入到另一个函数之中。
function fn(arg1, arg2, callback){
var num = Math.ceil(Math.random() * (arg1 - arg2) + arg2);
callback(num); //传递结果
}
fn(10, 20, function(num){
console.log("Callback called! Num: " + num);
}); //结果为10和20之间的随机数
我们上网去搜回调函数,给出的最简单的示例一般就是这样,看到这样的示例我一般会先通读一下代码,理解它的涵义。这个示例并不难理解,fn是一个声明的函数,callback是一个函数类型的参数,它将在函数中被调用,但是它的实现会在它调用的时候才会确定。但是理解之后我就随之冒出一个问题,这样写是多此一举把?我为什么不直接在函数fn中写一个console就好了,而用这样复杂的回调函数形式写出这么冗杂难读的代码呢?
这里就得思考一下回调函数的意义了,有人曾解释过回调函数,说之所以使用回调函数,是因为再声明回调的时候,使用者自己都不知道回调函数要干什么。我觉得这种解释有一定道理,但是不够准确。如果,我完全不知道某个函数要干嘛,那我为什么要这个函数写在这里呢?所以说,这个概念本身很模糊,以至于我写了多少年也没有手动原生写过一个回调函数。直到上周我在实际开发中突然灵光一闪使用到了这个久违的功能,才大致明白了回调函数存在的意义,以下基本是我想到的可以使用回调的情形。(emmmm,这些情形不是示例中那样强行回调,而是你在这些情况使用回调会觉得,这里使用回调真的是太酷炫,太省事啦)
1.定时器。这个是函数内部已经实现的功能,我们在使用setTimeout和setInterval的时候其实就是传入了一个回调函数。不过扩展一下,其实,这种情形是符合那种完全不知道函数该干什么的情况,但是至少我们知道这个函数是要多少时间后执行,这样的情况我认为一般只会出现像定时器一样这种比较底层的设计之中,在具体使用回调的时候,这样的情况太难把控了。这可以总结为第一种情况,只知道模糊的回调前提,基本对回调函数没有什么多余的限制,全部交给回调函数的编写者,比较难以把控。
2.修改指向性变量(我不知道要改什么)。这一个是我这一周遇到的情况,我有一个需要改变的对象数组panelsData,panelsData的每一项(暂时写作panelData,我在代码中也是这么写的)panelData都有很多的属性,而且这个对象是要传入到微信小程序的wxml之中的,也就意味着我如果需要修改panelsData中的某一项,那么必须先要创建一个克隆对象,在修改之后再进行一次setData。
现在解释到这里,我们已经有了大致的函数流程
比较麻烦的就是修改对象中的某一项的某个属性,因为这个会根据实际情形出现不同的情况,我一开始是想通过传入参数改变的,但是感觉传入进去,会产生多种麻烦(当然现在想来,有对象字面量的话,似乎是我考虑不周)。这时就轮到回调函数登场了,这里只需要一个callback,callback的具体功能就是修改panelsData中的某一个panelData的某一个属性,好了,剩下的留到实际使用的时候决定吧,毕竟那时候可以确定是哪一项哪一个属性,直接选取赋值就好了。
3.数组遍历类型(类似underscore,我不知道要怎么改)。最后一种与上一种其实是反过来的,我们知道要改动的对象是什么,但是我们不知道要如何改动,就像underscore.js中的内容,我们不知道使用者要对这个数组做什么,所以留下一个供使用者发挥自己功能的函数,除了对这个数组进行遍历,使用者还可以对他们进行额外的操作。
_.map([1, 2, 3], function(num){ return num * 3; });
=> [3, 6, 9]
_.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; });
=> [3, 6, 9]
_.map([[1, 2], [3, 4]], _.first);
=> [1, 3]
4.最后是一些比较表层的简化方式,这里先不细说了,不过也很重要:添加注释,统一命名!!
结语:简化函数的路真的是茫茫无边,因为代码的优化是无止尽的嘛。这一周还是没用markdown,下周写一个markdown初步学习把!!~~