ctrl+c 和 ctrl+v 給咱們帶來了不少的便利,可是也使咱們變得懶惰,不肯思考。
相信不少人和我同樣,在開發項目的時候,由於項目趕,或者一時沒想到等緣由。頻繁使用 ctrl+c 和 ctrl+v ,致使代碼不少都是重複的。這幾天,也看了本身之前寫的代碼,簡單的探索了一下,挑選幾個實例,分享下如何在特定場景下,保證代碼質量前提下,提升代碼複用性。css
提升代碼的複用性,應該是不一樣場景,不一樣解決方案的。同時也要保證代碼質量。不建議強制提升代碼複用性,若是提升代碼複用性會大大的下降代碼的可讀性,維護性,可能會得不償失。
在作項目的時候,相信頁面上總會有不少類似的按鈕,好比下圖,項目上幾個類似的按鈕。面對這樣的需求,以前是直接寫三個按鈕html
<button type="button" class='u-btn-yes-samll'>肯定</button> <button type="button" class='u-btn-yes'>肯定</button> <button type="button" class='u-btn-yes-big'>肯定</button>
css前端
button{ border:none; font-family:'微軟雅黑'; } .u-btn-yes{ width:80px; height:36px; font-size:14px; color:#fff; border-radius:10px; background:#09f; } .u-btn-yes-samll{ width:60px; height:30px; font-size:12px; color:#fff; border-radius:10px; background:#09f; } .u-btn-yes-big{ width:100px; height:40px; font-size:16px; color:#fff; border-radius:10px; background:#09f; }
這樣至關於每增長一種按鈕,就增長了幾行的 css 代碼,實際上改變的只有 width ,height ,font-size 這三個屬性。數組
實際上能夠根據大小進行代碼複用。微信
.u-btn-yes{ width:80px; height:36px; font-size:14px; color:#fff; border-radius:10px; background:#09f; } .u-btn-yes.u-btn-samll{ width:60px; height:30px; font-size:12px; } .u-btn-yes.u-btn-big{ width:100px; height:40px; font-size:16px; }
頁面調用編輯器
<button type="button" class='u-btn-yes u-btn-samll'>肯定</button> <button type="button" class='u-btn-yes'>肯定</button> <button type="button" class='u-btn-yes u-btn-big'>肯定</button>
頁面上可能還有不少按鈕相似的,可是不一樣顏色的(以下圖),也能夠靈活處理,這樣還能夠自由組合。這個也是社區上不少 UI 庫所使用的方式。函數
.u-btn{ width:80px; height:36px; font-size:14px; color:#fff; border-radius:10px; background:#09f; } .u-btn-samll{ width:60px; height:30px; font-size:12px; } .u-btn-big{ width:100px; height:40px; font-size:16px; } .u-btn-red{ background:#f33; } .u-btn-yellow{ background:#f90; }
html優化
<button type="button" class='u-btn u-btn-samll'>肯定</button> <button type="button" class='u-btn u-btn-red'>肯定</button> <button type="button" class='u-btn u-btn-big u-btn-yellow'>肯定</button>
對於這些按鈕,不建議設置 margin ,positon 等樣式,由於不一樣的按鈕在不一樣的地方,上面這幾個屬性基本不會同樣。
關於提升代碼複用性的好處,在上面 HTML+CSS的實例裏面並無很明顯的優點,但在 JS 裏面提升代碼的複用性優點就比較明顯了,下面簡單列舉幾個例子。this
在上家公司的項目裏面有這樣代碼,目的也很明顯,就是用戶填寫表單的時候,尚未填寫完整就提交,前端這裏就須要給一個簡單的提示。spa
//當信息沒填寫完整的時候彈出提示 layer.alert('請檢查信息是否填寫完整',{ title:'提示', icon:2 })
基於 layer 這個開源庫,代碼看得特別的簡單。可是隨着項目的開發,用戶填寫表單的地方有多個,那麼上面的代碼就會被複制到多個地方,這樣不免會有有點多餘。
另外,這樣作最大的一個問題就是:若是上面的代碼在項目上有20個地方在用,有一天需求變了,title 這個屬性值要從‘提示’變成‘警告’。那就麻煩了,要找20個地方,即便編輯器有全局替換的功能,這樣的改動出問題的機率也比較大。面對這樣的狀況。以前處理的方法是對這個彈窗進行簡單粗暴的封裝,方便複用。
function openTips(){ //當信息沒填寫完整的時候彈出提示 layer.alert('請檢查信息是否填寫完整',{ title:'提示', icon:2 }); }
在須要的地方,須要的時候進行調用就好,這樣能夠寫少不少代碼!修改起來,也只須要修改 openTips 這一個地方就完事了。
openTips();
下面再看一個實例。借用下以前羣友的發的代碼。
模擬數據
let listWarnConf = [ { warnType: 1, warnCondition: 220, }, { warnType: 2, warnCondition: 36, }, { warnType: 3, warnCondition: 45, }, { warnType: 4, warnCondition: 110, }, { warnType: 5, warnCondition: 380, } ]
業務邏輯代碼
listWarnConf.forEach(item => { switch(item.warnType) { case 1: item.warnTypeText = '超壓'; item.warnConditionText = `電壓高於${item.warnCondition}V` break; case 2: item.warnTypeText = '欠壓'; item.warnConditionText = `電壓低於${item.warnCondition}V` break case 3: item.warnTypeText = '超載'; item.warnConditionText = `電流高於${item.warnCondition}A` break case 4: item.warnTypeText = '電壓不平衡'; item.warnConditionText = `電壓不平衡高於${item.warnCondition}%` break case 5: item.warnTypeText = '電流不平衡'; item.warnConditionText = `電流不平衡${item.warnCondition}%` break } })
這樣看着結果是沒問題的,可是看着那麼多 case 執行的都是賦值操做。並且最大的問題和上面同樣,若是多個地方使用,需求變了,那麼仍是要修改這麼多的地方,下面優化下,讓代碼的複用性提升下。
//設置配置數據 let warnConfig={ 1:{ warnTypeText:'超壓', warnConditionText:'電壓高於replaceTextV' }, 2:{ warnTypeText:'欠壓', warnConditionText:'電壓低於replaceTextV' }, 3:{ warnTypeText:'超載', warnConditionText:'電流高於replaceTextV' }, 4:{ warnTypeText:'電壓不平衡', warnConditionText:'電壓不平衡高於replaceText%' }, 5:{ warnTypeText:'電流不平衡', warnConditionText:'電流不平衡高於replaceText%' } } //業務邏輯--根據配置數據設置warnTypeText和warnConditionText listWarnConf.forEach(item => { item.warnTypeText=warnConfig[item.warnType].warnTypeText; item.warnConditionText=warnConfig[item.warnType].warnConditionText.replace('replaceText',item.warnCondition); })
這樣改代碼量沒減小,可讀性比 switch 差,但能讀懂。可是這樣作就是重複的代碼少了,配置數據和業務邏輯分離了,若是之後要修改配置數據或者業務邏輯,就修改其中一項便可,互相不影響。把配置數據抽出來公用,那麼在須要修改的時候,直接修改就好。
關於提升代碼的複用性,或者說減小重複的代碼,我的覺能夠往如下目標努力--當需求發生改變,須要修改代碼的時候,一樣的代碼不要修改兩次。
保持函數的單一職責,保證一個函數只執行一個動做,每一個動做互不影響,能夠自由組合,就能夠提升代碼的複用性。
好比下面的代碼,從服務端請求回來的訂單數據以下,須要進行如下處理
1.根據 status 進行對應值得顯示(0-進行中,1-已完成,2-訂單異常)
2.把 startTime 由時間戳顯示成 yyyy-mm-dd
3.若是字段值爲空字符串 ,設置字段值爲 ‘--’
let orderList=[ { id:1, status:0, startTime:1538323200000, }, { id:2, status:2, startTime:1538523200000, }, { id:3, status:1, startTime:1538723200000, }, { id:4, status:'', startTime:'', }, ];
需求彷佛很簡單,代碼也少
let _status={ 0:'進行中', 1:'已完成', 2:'訂單異常' } orderList.forEach(item=>{ //設置狀態 item.status=item.status.toString()?_status[item.status]:''; //設置時間 item.startTime=item.startTime.toString()?new Date(item.startTime).toLocaleDateString().replace(/\//g,'-'):''; //設置-- for(let key in item){ if(item[key]===''){ item[key]='--'; } } })
運行結果也正常,可是這樣寫代碼重複性會不少,好比下面,另外一組重服務端請求回來的用戶數據,用戶數據沒有 status,startTime,兩個字段,並且須要根據 type 對應顯示用戶的身份(0-普通用戶,1-vip,2-超級vip)。
let userList=[ { id:1, name:'守候', type:0 }, { id:2, name:'浪跡天涯', type:1 }, { id:3, name:'曾經', type:2 } ]
出現這樣的需求,以前寫的代碼沒法重用,只能複製過來,再修改下。
let _type={ 0:'普通用戶', 1:'vip', 2:'超級vip' } userList.forEach(item=>{ //設置type item.type=item.type.toString()?_type[item.type]:''; //設置-- for(let key in item){ if(item[key]===''){ item[key]='--'; } } })
結果正常,想必你們已經發現問題了,代碼有點多餘。下面就使用單一職責的原則改造下操做函數,設置 status,startTime,type,-- 。這裏拆分紅四個函數。
let handleFn={ setStatus(list){ let _status={ 0:'進行中', 1:'已完成', 2:'訂單異常' } list.forEach(item=>{ item.status=item.status.toString()?_status[item.status]:''; }) return list }, setStartTime(list){ list.forEach(item=>{ item.startTime=item.startTime.toString()?new Date(item.startTime).toLocaleDateString().replace(/\//g,'-'):''; }) return list; }, setInfo(list){ list.forEach(item=>{ for(let key in item){ if(item[key]===''){ item[key]='--'; } } }) return list; }, setType(list){ let _type={ 0:'普通用戶', 1:'vip', 2:'超級vip' } list.forEach(item=>{ item.type=item.type.toString()?_type[item.type]:''; }) return list; } }
下面直接調用函數就好
//處理訂單數據 orderList=handleFn.setStatus(orderList); orderList=handleFn.setStartTime(orderList); orderList=handleFn.setInfo(orderList); console.log(orderList); //處理用戶數據 userList=handleFn.setType(userList); userList=handleFn.setInfo(userList); console.log(userList);
運行結果也正常
若是嫌棄連續賦值麻煩,能夠借用 jQuery 的那個思想,進行鏈式調用。
let ec=(function () { let handle=function (obj) { //深拷貝對象 this.obj=JSON.parse(JSON.stringify(obj)); }; handle.prototype={ /** * @description 設置保密信息 */ setInfo(){ this.obj.map(item=>{ for(let key in item){ if(item[key]===''){ item[key]='--'; } } }); return this; }, /** * @description 設置狀態 */ setStatus(){ let _status={ 0:'進行中', 1:'已完成', 2:'訂單異常' } this.obj.forEach(item=>{ item.status=item.status.toString()?_status[item.status]:'' }); return this; }, /** * @description 設置時間 */ setStartTime(){ this.obj.forEach(item=>{ item.startTime=item.startTime.toString()?new Date(item.startTime).toLocaleDateString().replace(/\//g,'-'):''; }); return this; }, /** * @description 設置type */ setType(){ let _type={ 0:'普通用戶', 1:'vip', 2:'超級vip' } this.obj.forEach(item=>{ item.type=item.type.toString()?_type[item.type]:''; }) return this; }, /** * @description 返回處理結果 * @return {Array|*} */ end(){ return this.obj; } } //暴露構造函數接口 return function (obj) { return new handle(obj); } })();
這樣就能夠鏈式調用了
//處理訂單數據 orderList=ec(orderList).setStatus().setStartTime().setInfo().end(); console.log(orderList); //處理用戶數據 userList=ec(userList).setType().end(); console.log(userList);
事情到這裏了,相信你們發現一個很嚴重的問題就是循環的次數增長了。沒優化以前,只須要循環一次,就能夠把設置狀態,設置時間,設置--這些步驟都完成,可是如今 setStatus().setStartTime().setInfo()
這裏的代碼,每執行一個函數,都遍歷了一次數組,這個就得優化下。處理的方式就是在每個函數裏面,只記錄要處理什麼,可是不進行處理,等到執行到 end 的時候再統一處理,以及返回。
let ec=(function () { let handle=function (obj) { //深拷貝對象 this.obj=JSON.parse(JSON.stringify(obj)); //記錄要處理的步驟 this.handleFnList=[]; }; handle.prototype={ /** * @description 設置保密信息 */ handleSetInfo(item){ for(let key in item){ if(item[key]===''){ item[key]='--'; } } return this; }, setInfo(){ this.handleFnList.push('handleSetInfo'); return this; }, /** * @description 設置狀態 */ handleSetStatus(item){ let _status={ 0:'進行中', 1:'已完成', 2:'訂單異常' } item.status=item.status.toString()?_status[item.status]:'' return item; }, setStatus(){ this.handleFnList.push('handleSetStatus'); return this; }, /** * @description 設置時間 */ handleSetStartTime(item){ item.startTime=item.startTime.toString()?new Date(item.startTime).toLocaleDateString().replace(/\//g,'-'):''; return item; }, setStartTime(){ this.handleFnList.push('handleSetStartTime'); return this; }, /** * @description 設置type */ handleSetType(item){ let _type={ 0:'普通用戶', 1:'vip', 2:'超級vip' } item.type=item.type.toString()?_type[item.type]:''; return item; }, setType(){ this.handleFnList.push('handleSetType'); return this; }, /** * @description 返回處理結果 * @return {Array|*} */ end(){ //統一處理操做 this.obj.forEach(item=>{ this.handleFnList.forEach(fn=>{ item=this[fn](item); }) }) return this.obj; } } //暴露構造函數接口 return function (obj) { return new handle(obj); } })();
這樣改,以前的調用方式不須要改變,而後結果也是正確的
可能你們會以爲很簡單一個需求,卻搞得這麼複雜。若是這樣想是正確的,由於這個的確搞複雜了,可讀性也差了,但想到項目遇到的處理數據不止這一些,還有好比金額的格式顯示,其它數據的各類狀態碼解析顯示,銀行卡號每隔4位分割,電話號碼的顯示等等。因此就先封裝一下,之後用的時候,直接使用。不知道算不算是先苦後甜?若是需求比較簡單,可能真的不必這麼樣封裝。
假期看代碼,提升代碼複用性的總結,差很少就是這些了,固然還有一些實例,可是在以前已經寫過了,和該文章說起的實例也是大同小異,就再也不重複說起。提升代碼的複用性是一個很大的話題,若是你們有什麼好的建議,實例,歡迎分享。
-------------------------華麗的分割線--------------------
想了解更多,和我交流,內推職位,請添加我微信。或者關注個人微信公衆號:守候書閣