提煉函數(shù)
這個方法是我們最經(jīng)常做的優(yōu)化,我們希望在編程過程中,函數(shù)都有良好的命名,而且在函數(shù)的內(nèi)部包含清晰的邏輯阔馋,我們在日常編程的過程中,我們往往會向一個函數(shù)中塞入大量的代碼娇掏,使其違反了單一變量的原則呕寝。我們不得不在函數(shù)內(nèi)部加入若干的注釋,讓這個函數(shù)顯得容易讀懂一些驹碍,這個時候壁涎,我們就需要對代碼進(jìn)行簡化。我們的做法是:將一些代碼獨立出志秃,放入到另外一個單獨的函數(shù)中怔球。這樣做有如下幾個優(yōu)點:
- 避免出現(xiàn)超大的函數(shù)
- 獨立出來的代碼方便進(jìn)行代碼的復(fù)用
- 獨立出來的函數(shù)更加容易被覆寫
- 獨立出來的函數(shù)如果擁有一個良好的命名,它本身就起到了一個注釋的作用浮还。
我們用一段代碼來舉個栗子竟坛,我們只一個負(fù)責(zé)獲取用戶信息的函數(shù)里面,我們還需要打印用戶信息相關(guān)的log钧舌。我們將打印的語句封裝到一個獨立的函數(shù)里面:
//優(yōu)化前
var getUserInfo = function(){
ajax('http://XXX.com/userInfo', function( data){
console.log('userId:' + data.userId);
console.log('userName:' + data.userName);
console.log('nickName:' + data.nickName);
});
};
//優(yōu)化后
var getUserInfo = function(){
ajax('http://XXX.com/userInfo', function( data){
printDetail(data);
});
};
var printDetail = function(data){
console.log('userId:' + data.userId);
console.log('userName:' + data.userName);
console.log('nickName:' + data.nickName);
};
合并重復(fù)的條件判斷
如果我們在函數(shù)的內(nèi)部有一些條件分支語句担汤,然后在這些條件分支語句中散布了一些重復(fù)的代碼,那么我們就有必要做一些合并的工作洼冻、現(xiàn)在給出一個應(yīng)用的場景:假設(shè)我們有一個分頁的函數(shù)paging
崭歧,這個函數(shù)接受一個參數(shù)currPage
,currPage
表示要跳轉(zhuǎn)的頁碼撞牢,在跳轉(zhuǎn)之前要防止currPage
過大或是過小率碾,我們要進(jìn)行手動的修正∥荼耄看一段偽代碼:
//優(yōu)化前
var paging = function(){
if(currPage <= 0){
currPage = 0;
jump(currPage);
}else if(currPage >= totalPage){
currPage = totalpage;
jump(currPage);
}else{
jump(currPage);
}
}
上面的代碼我們可以看到所宰,負(fù)責(zé)跳轉(zhuǎn)的代碼jump(currPage)
在每一個條件分支中都出現(xiàn)了,所以完全可以把這部分的代碼獨立出來
//優(yōu)化后
var paging = function (currPage){
if(currPage <= 0){
currPage = 0;
}else if (currPage > = totalPage){
currPage = totalPage;
}
jump(currPage);
}
將條件分支語句提煉成函數(shù)
我們在編程過程中畜挥,可能看到別人的代碼有大連的if-else
語句仔粥,導(dǎo)致最后代碼的可讀性很差。舉一個生活中的例子:現(xiàn)在有一個計算商品的函數(shù),計算的規(guī)則是躯泰,如果當(dāng)前季節(jié)處于夏季谭羔,那么全部的商品將以8折出售,代碼如下
var getPrice = function(price){
var date = new Date();
if(date.getMonth() >= 6 && date,getMonth() <= 9){
return price * 0.8
}
};
我們要判斷的表達(dá)的意思是很簡單的斟冕,就是判斷當(dāng)前是否為夏季口糕,但是我們在if的語句中缅阳,很難得到代碼想要表達(dá)的意思磕蛇。這個時候我們可以將這段代碼提煉成一個單獨的函數(shù),就可以更加準(zhǔn)確地表達(dá)代碼的意思十办,函數(shù)本身的函數(shù)名也可以起到注釋的作用秀撇。
var isSummer = function(){
var date = new Date();
return date.getMonth()>6 && date.getMonth() < 9
}
var getPrice = function(price){
var date = new Date();
if(isSummer()){
return price * 0.8
}
};
合理的使用循環(huán)
在函數(shù)體的內(nèi)部,會有很多的重復(fù)性的工作向族,那么合理的使用循環(huán)呵燕,就是我們需要考慮的問題,使用循環(huán)可以減少代碼量件相。加入我們創(chuàng)建XHR
對象的代碼再扭,在這里就不考慮瀏覽器的兼容性了。
var createXHR = function(){
var xhr;
try{
xhr = new ActiveXObject('MSXML2.XMLHTTP.6.0');
}cache(e){
try{
xhr = new ActiveXObject('MSXML2.XMLHTTP.3.0');
}cache(e){
xhr = new ActiveXObject('MSXML2.XMLHTTP');
}
}
return xhr;
}
var xhr = createXHR ();
現(xiàn)在我們對上面的代碼進(jìn)行優(yōu)化夜矗,巧妙的使用循環(huán)泛范,達(dá)到和上面代碼一樣的效果:
var createXHR = function(){
var versions = ['MSXML2.XMLHTTP.6.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP'];
for(let i = 0,version; version = versions[i++]){
try{
return new ActiveXObject(version);
}cache(e){
}
}
};
var xhr = createXHR ();
提前讓函數(shù)退出代替嵌套條件分支
我們的編程習(xí)慣讓我們有這樣的觀念:“每一個函數(shù)只能有一個出口和入口”∥伤海現(xiàn)代的編程語言都會限制函數(shù)只有一個入口罢荡,但是對于函數(shù)只有一個出口都是有不同的看法。
var del = function(obj){
var ret;
if(!obj.isReadyOnly){//文件是只讀模式
if(obj.isFloder){//是文件夾
ret = deleteFloder(obj);
}else if(obj.isFile){//是文件形式
ret = deleteFile(obj)
}
}
return ret;
};
嵌套的條件分支語句絕對是代碼維護(hù)者的噩夢对扶,因為邏輯看上去十分的混亂区赵,理解上十分的困難,有的時候外層的if分支在左括號和有括號相隔很遠(yuǎn)浪南,理解上就更加復(fù)雜了額笼才,我們之前在編程的時候,一直堅信络凿,一個函數(shù)只有一個出口骡送,實際上程序?qū)κO虏糠值倪壿嫴⒉魂P(guān)注,所以這個時候可以立即退出喷众,不會引導(dǎo)程序員去看一些無用的else
的語句各谚。
var del = function(obj){
var ret;
if(!obj.isReadyOnly){//文件是只讀模式
return;
}
if(obj.isFloder){//是文件夾
return deleteFloder(obj);
}
if(obj.isFile){//是文件形式
return deleteFile(obj)
}
};
傳遞參數(shù)對象代替過長的參數(shù)列表
有的時候,一個函數(shù)可以接受多個參數(shù)到千,而且參數(shù)的數(shù)量越多昌渤,函數(shù)的功能越難理解。因為使用這個函數(shù)的用戶必須知道各個參數(shù)的作用是什么憔四,使用的時候膀息,還要小心謹(jǐn)慎般眉,避免多傳或是少傳參數(shù),造成錯誤潜支,而且當(dāng)我們想要添加參數(shù)的時候甸赃,設(shè)計到很多的代碼的修改。
var setUserInfo = function(id,name, address, sex, mobile,qq){
console.log('id:'+id);
console.log('name:'+name);
console.log('address:'+address);
console.log('sex:'+sex);
console.log('mobile:'+mobile);
console.log('qq:'+qq);
}
setUserInfo (1212, 'kim','shanghai','female','123456788912',12345678978)
我們可以將參數(shù)放入到一個對象里面冗酿,然后在將對象傳入到函數(shù)中埠对,而且不用再傳參的時候關(guān)心參數(shù)的順序和數(shù)量,只要保證參數(shù)的key
值不變就可以了裁替。
var setUserInfo = function(obj){
console.log('id:'+id);
console.log('name:'+name);
console.log('address:'+address);
console.log('sex:'+sex);
console.log('mobile:'+mobile);
console.log('qq:'+qq);
}
setUserInfo ({
id:1212,
name:'kim',
address:'shanghai',
sex:'female',
mobile:'123456788912',
qq:12345678978
})
盡減少參數(shù)的數(shù)量數(shù)量
如果我們向一個函數(shù)中傳入很多的參數(shù)项玛,那么我們使用的時候,要先搞懂參數(shù)的意義弱判,這樣很浪費時間襟沮。但是在實際的開發(fā)過程中,向函數(shù)傳入?yún)?shù)是不可避免的昌腰,我們假設(shè)一下下面的應(yīng)用場景:有一個畫圖的函數(shù)draw
开伏,他現(xiàn)在只可以繪制長方形,接受了3個參數(shù)遭商。分別是長寬和面積固灵,但是我們知道,面積是可以通過長和寬計算出來的株婴。
var draw = function(width怎虫, height,width)
所以我們對上面的代碼進(jìn)行優(yōu)化困介,將square參數(shù)從函數(shù)中去掉
var draw = function(width大审, height){
var square = width * height;
}
假設(shè)日后這個draw
函數(shù)支持繪制原型,我們就需要把長和寬的參數(shù)換成半徑radius
座哩,但是圖形的面積還是不應(yīng)該由客戶端進(jìn)行傳入徒扶,而是應(yīng)該在draw
函數(shù)的內(nèi)部,由一定的規(guī)則進(jìn)行計算根穷。這個時候就可以使用策略模式姜骡,讓draw
函數(shù)支持說中圖形的繪制。
慎用三目運算符
有一些程序員喜歡使用三目運算符來代替if-else
的語句屿良,因為代碼量少圈澈,運算性能高。但是三目運算符的運算性能并不比if-else
高很多尘惧。而且在負(fù)責(zé)的邏輯中使用三目運算符會降低代碼的可讀性和可維護(hù)性康栈。而且讓js文件加載的速度加快的方法有很多。比如說壓縮、緩存等等啥么,但是僅僅把關(guān)注點放在使用三目運算符的數(shù)目上登舞,無異于將一個300斤超重的胖子的超重原因歸結(jié)到了頭皮屑上面。
什么時候使用三目運算符:當(dāng)條件分支的邏輯十分的簡單清楚悬荣。
var global = typeof window !== 'undefined'? window:this;
但是如果我們的條件十分的復(fù)雜菠秒,我們還是按部就班的寫if語句比較好
if(!aup || bup){
return a === doc ? -1:
b=== doc ? 1:
aup ? -1:
bup ? 1:
sortInput ?
(indexOf.call (sortInput,a) - indexOf.call(sortInput,b)):
0
}
合理的使用鏈?zhǔn)秸{(diào)用
經(jīng)常使用jquery的程序員比較習(xí)慣使用鏈?zhǔn)秸{(diào)用的寫法,在JavaScript
中氯迂,可以很容易的實現(xiàn)鏈?zhǔn)秸{(diào)用践叠,即讓方法調(diào)用結(jié)束后返回對象自身。
var User = function (){
this.id = null;
this.name = null;
};
User.prototype.setId = function (id){
this.id = id;
return this;
}
User.prototype.setName = function (name){
this.name= name;
return this;
}
console.log(new User().setId(1212).setName('kim'));
//寫法二
var User = {
id : null,
name: null,
setId: function(id){
this.id = id;
return this;
}
setId: function(name){
this.name = name;
return this;
}
};
console.log(User.setId(1212).setName('kim'));
通常來說囚戚,臉是調(diào)用的方式并不會造成閱讀理解時候的困難酵熙,也可以減少一些中間變量,但是節(jié)省下來的字節(jié)幾乎可以忽略布局驰坊。鏈?zhǔn)秸{(diào)用帶來的壞處就是調(diào)試的時候十分的不方便,只要中間一節(jié)出現(xiàn)了錯誤哮独,必須將整條鏈拆開拳芙,再添加斷點,才可以定位錯誤出現(xiàn)的位置皮璧。
用return退出多重循環(huán)
假設(shè)函數(shù)體內(nèi)有一個兩重的循環(huán)語句舟扎,我們需要內(nèi)層循環(huán)中判斷,當(dāng)?shù)竭_(dá)某個臨界條件時退出外層的循環(huán)悴务。我們大多數(shù)時候會引入一個控制標(biāo)記變量:
var func = function(){
var flag = false;
for(let i = 0; i< 10 ; i++){
for(let j = 0; j < 10; j++){
if(i *j > 30){
flag = true;
break;
}
}
if(flag === true){
break;
}
}
};
第二種寫法是設(shè)置循環(huán)標(biāo)記:
var func = function(){
outerloop:
for(let i = 0; i< 10; i++){
innerloop:
for(let j = 0; j<10; j++){
if(i * j >30){
break outerloop;
}
}
}
}
這兩種做法都沒有錯睹限,但是還有更加簡單的做法,就是在終止循環(huán)的時候讯檐。直接的退出整個方法:
var func = function(){
for(let i = 0; i <10;i++){
for (let j = 0;j <10; j++){
if(i *j >30){
return;
}
}
}
};
當(dāng)然直接這么寫又會帶來一個新的問題羡疗,就是如果在循環(huán)后還有一些將要被執(zhí)行的代碼,直接退出整個方法别洪,這些方法就不會有被執(zhí)行的機會了叨恨,舉個栗子:
var func = function(){
for(let i = 0; i <10;i++){
for (let j = 0;j <10; j++){
if(i *j >30){
return;
}
}
}
console.log(i);
};
為了我們解決這個問題,我們將循環(huán)后的戴拿直接放在return的后面挖垛,如果代碼的比較多痒钝,就直接將其提煉成一個函數(shù)。
var print = function(i){
console.log(i)
} ;
var func = function(){
for(let i = 0; i <10;i++){
for (let j = 0;j <10; j++){
if(i *j >30){
return print (i);
}
}
}
};
func();