重構代碼的tricks
來自: http://segmentfault.com/a/1190000004360784
js的設計模式是針對于整體代碼的設計是否合理,給出了一些具體的解決辦法。 而重構代碼就是依賴于設計模式而實現的一個必要手段,可以說設計模式就是重構代碼的目標,但他的手段卻不僅僅只有設計模式這些大而全的,同樣存在小而精,我們隨處可以使用的。
封裝功能塊代碼
我們通常在寫代碼的時候,一開始,并不需要考慮太多。在后期可以進行修改和提煉。 比如,我有個業務需求,是創建一個div并且渲染數據到頁面上,并且根據data的不同,改變div的狀態.
function(data){ var div = document.createElement("div"), div.innerHTML = data.name; document.body.append(div); if(data.isShow){ div.style.display = "block"; }else{ div.style.display = "none"; } }
但事實上,這個函數里面還有一個代碼塊,就是根據data.isShow改變div的狀態.我們可以對其進行封裝。
function(data){ var div = document.createElement("div"), div.innerHTML = data.name; document.body.append(div); changeState(data.isShow,div); } function changeState(flag,div){ div.style.display = flag?"block":"none"; }
提取公因式
這里主要針對于,多次重復調用同一個封裝代碼塊函數。
function(flag){ if(flag==="left"){ move("right"); }else if(flag==="right"){ move("left"); }else if(flag==="top"){ move("bottom"); }else if(flag==="bottom"){ move("top"); } }
根據flag向反方向移動,可以看出,里面都有用到了move()這個方法,要知道,分支語句是最不利于程序閱讀的,而且我們要盡可能的減少和簡化分支語句里面的程序量,讓閱讀者耗費在分支語句上的時間降到最少。上面代碼可以這樣寫。
function(flag){ var dir; if(flag==="left"){ dir = "right"; }else if(flag==="right"){ dir = "left"; }else if(flag==="top"){ dir = "bottom"; }else if(flag==="bottom"){ dir = "top"; } move(dir); }
恩,當然,這樣寫也是違反人性的。我們可以使用命令模式進行重構。這就涉及到另外一個tip.
將分支轉化為函數
上面代碼里面的分支完全可以使用函數來進行代替。
function(flag){ command.flag; } var command = (function(){ var left = function(){ move("right"); } var right = function(){ move("left"); } var top = function(){ move("bottom"); } var bottom = function(){ move("top"); } return { left,right,top,bottom } })();
這樣,雖然增加了一個對象,但是代碼確實清晰可見的。 這就是通過命令模式,來重構代碼,完成性能和閱讀的優化。但有時候,使用分支,會比這樣更簡潔,那當然可以使用分支啦。 而使用分支還要主意一個tip就是.
不要過度嵌套
這里想說的就兩點,一是,盡可能不使用分支,二是,如果嵌套分支,盡量改為不嵌套。 不使用分支的情況上面已經說了,如果使用分支,那么請不要嵌套,或者說不要過度嵌套。因為一個分支已經很難閱讀了,md,你再加個嵌套,你還讓不讓人讀了。 而解決過度嵌套的方法真的是千千萬萬,我這里就介紹一個比較簡單的。使用return 提早退出嵌套。
function move(obj){ if(obj.isShow){ if(obj.isTop){ if(obj.isLeft){ return move("TopLeft"); } }else{ return false; } }else { return false; } }
這,看著爽不爽。 如果,我遇見這樣的代碼,我第一反應就是,要!死!啦!. 所以,為了讓你的程序人性化,我們可以使用return 語句進行改寫。 我們可以對條件判斷的邏輯進行分析,可以看出,里面如果條件不滿足都是返回false,那么我們可以將false的情況提取出來。
function move(obj){ if(!obj.isShow){ return false; } if(!obj.isTop){ return false; } if(!obj.isLeft){ return false; } return move("TopLeft"); }
這是這個feel。當然,追求極致的話,我們可以看出return false,是完全一致的,當然可以將條件合并.
function move(obj){ if(!obj.isShow||!obj.isTop||!obj.isLeft){ return false; } return move("TopLeft"); }
其實如果你數學學得好的話(我還行吧,嘿~嘿~嘿~)。 這樣提取條件的事是輕而易舉的,可以看出,上面那段古老的代碼完全可以變為現在這個樣式,而且讀起來,真的不是一個檔次的。
減少參數數量
減少參數數量的方法,當然永遠不會===1, 因為每個人站的角度不同,得到的答案當然也不一樣。所以這里只介紹兩種比較通用的。
-
使用對象來代替參數列表。
-
將需要額外計算的參數忽略。
使用對象代替參數
這個最突出的特點就是在寫模板的時候。
function templ(name,age,gender){ return `my name is ${name}. and I'm ${age} years old. yeah! I am a ${gender}`; }
有一個模板,上面需求的參數有三個,但是,事實上,這個是完全不靠譜的。 因為一個人不僅僅只有name,age,gender 肯定還有別的參數,這樣,造成的后果就是,你一直在維護模板的同時,還需要維護參數列表。而且,還要維護,傳入參數的順序的正確性。所以這里強烈推薦使用對象來代替多參數。
function templ(person){ return `my name is ${person.name}. and I'm ${person.age} years old. yeah! I am a ${person.gender}`; }
現在這個模板函數與外界的耦合性已經降低了不少。而且非常易于維護,就算外面你的person對象有多余的參數,也不會妨礙我使用我需要的數據。
忽略額外計算的參數
這種情況主要是在做UI的時候可能會遇到,即,想繪制一個數據table的時候,需要將一個數據矩形的高,寬以及面積傳入一個函數,進行繪制。
function column(width,height,square){ console.log("矩形的寬度為"+width); console.log("矩形的高度為"+height); console.log("矩形的面積為"+square); }
而,這樣做是完全沒有必要的,因為函數參數越少,給人的感覺當然越好。我們可以修改為這樣.
function column(width,height){ var square = width*height; console.log("矩形的寬度為"+width); console.log("矩形的高度為"+height); console.log("矩形的面積為"+square); }
而且在插件設計中,也應該準遵守這個原則,函數的參數應該在能力范圍內,把它降至最少。
鏈式調用
這個應該算是比較高級的用法。使用過jQuery的同學應該印象最深刻。 即,我們可以這樣來使用一個功能.
$(".myClass").addClass("show").attr('data-name').css("display","none");
而這樣實現其實并不難,只要在每個方法的后面返回該對象就可以實現這個技能。我們來模仿一下。
var Div = function(){} Div.prototype.createElement = function(){ console.log("創建一個Div"); return this; } Div.prototype.showDiv = function(){ console.log("顯示Div"); return this; } var div = new Div(); div.createElement().showDiv();</pre>
這樣不僅可以實現對象的細粒度,而且也滿足單一職責原則。同樣,我要說的是,以為的使用鏈式的時候,記住,使用一個功能塊鏈式調用一定要分行,不然,調bug會調哭的。
var Div = function(){} Div.prototype.createElement = function(){ console.log("創建一個Div"); return this; } Div.prototype.showDiv = function(){ console.log("顯示Div"); return this; } Div.prototype.hideDiv = function(){ console.log("隱藏Div"); return this; } Div.prototype.tagName = function(){ console.log("tagName 是 Div"); return this; } var div = new Div(); div.createElement().showDiv().tagName().hideDiv(); //表這樣做</pre>
上面是個反例,正確的做法,應該分開。
div.createElement() .showDiv() .tagName() .hideDiv();像這樣調用,萬一出個Bug,你也應該知道這個bug在哪一個函數塊內。大部分重構的小技巧差不多介紹完了(智商有限),如果,大家有什么更好的建議歡迎留言反饋.
</div>