<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          編寫高質(zhì)量可維護的代碼之優(yōu)化邏輯判斷

          共 8467字,需瀏覽 17分鐘

           ·

          2020-08-28 06:22


          if else、switch case 是日常開發(fā)中最常見的條件判斷語句,這種看似簡單的語句,當遇到復(fù)雜的業(yè)務(wù)場景時,如果處理不善,就會出現(xiàn)大量的邏輯嵌套,可讀性差并且難以擴展。

          編寫高質(zhì)量可維護的代碼,我們先從最小處入手,一起來看看在前端開發(fā)過程中,可以從哪些方面來優(yōu)化邏輯判斷?

          下面我們會分別從 JavaScript 語法和 React JSX 語法兩個方面來分享一些優(yōu)化的技巧。

          JavaScript 語法篇

          嵌套層級優(yōu)化

          function?supply(fruit,?quantity)?{
          ??const?redFruits?=?['apple',?'strawberry',?'cherry',?'cranberries'];
          ??//?條件?1:?水果存在
          ??if(fruit)?{
          ????//?條件?2:?屬于紅色水果
          ????if(redFruits.includes(fruit))?{
          ??????console.log('紅色水果');
          ??????//?條件?3:?水果數(shù)量大于?10?個
          ??????if?(quantity?>?10)?{
          ????????console.log('數(shù)量大于?10?個');
          ??????}
          ????}
          ??}?else?{
          ????throw?new?Error('沒有水果啦!');
          ??}
          }

          分析上面的條件判斷,存在三層 if 條件嵌套。

          如果提前 return 掉無效條件,將 if else 的多重嵌套層次減少到一層,更容易理解和維護。

          function?supply(fruit,?quantity)?{
          ??const?redFruits?=?['apple',?'strawberry',?'cherry',?'cranberries'];
          ??if(!fruit)?throw?new?Error('沒有水果啦');?????//?條件?1:?當?fruit?無效時,提前處理錯誤
          ??if(!redFruits.includes(fruit))?return;?//?條件?2:?當不是紅色水果時,提前?return
          ????
          ??console.log('紅色水果');
          ????
          ??//?條件?3:?水果數(shù)量大于?10?個
          ??if?(quantity?>?10)?{
          ????console.log('數(shù)量大于?10?個');
          ??}
          }

          多條件分支的優(yōu)化處理

          當需要枚舉值處理不同的業(yè)務(wù)分支邏輯時,第一反應(yīng)是寫下 if else ?我們來看一下:

          function?pick(color)?{
          ??//?根據(jù)顏色選擇水果
          ??if(color?===?'red')?{
          ????return?['apple',?'strawberry'];?
          ??}?else?if?(color?===?'yellow')?{
          ????return?['banana',?'pineapple'];
          ??}?else?if?(color?===?'purple')?{
          ????return?['grape',?'plum'];
          ??}?else?{
          ????return?[];
          ??}
          }

          在上面的實現(xiàn)中:

          • if else 分支太多
          • if else 更適合于條件區(qū)間判斷,而 switch case 更適合于具體枚舉值的分支判斷

          使用 switch case 優(yōu)化上面的代碼后:

          function?pick(color)?{
          ??//?根據(jù)顏色選擇水果
          ??switch?(color)?{
          ????case?'red':
          ??????return?['apple',?'strawberry'];
          ????case?'yellow':
          ??????return?['banana',?'pineapple'];
          ????case?'purple':
          ??????return?['grape',?'plum'];
          ????default:
          ??????return?[];
          ??}
          }

          switch case 優(yōu)化之后的代碼看上去格式整齊,思路很清晰,但還是很冗長。繼續(xù)優(yōu)化:

          • 借助 Object 的 { key: value } 結(jié)構(gòu),我們可以在 Object 中枚舉所有的情況,然后將 key 作為索引,直接通過 Object.key 或者 Object[key] 來獲取內(nèi)容
          const?fruitColor?=?{????????????????????????
          ??red:?['apple',?'strawberry'],
          ??yellow:?['banana',?'pineapple'],
          ??purple:?['grape',?'plum'],
          }
          function?pick(color)?{
          ??return?fruitColor[color]?||?[];
          }
          • 使用 Map 數(shù)據(jù)結(jié)構(gòu),真正的 (key, value) 鍵值對結(jié)構(gòu);

          const?fruitColor?=?new?Map()
          .set('red',?['apple',?'strawberry'])
          .set('yellow',?['banana',?'pineapple'])
          .set('purple',?['grape',?'plum']);

          function?pick(color)?{
          ??return?fruitColor.get(color)?||?[];
          }

          優(yōu)化之后,代碼更簡潔、更容易擴展。

          為了更好的可讀性,還可以通過更加語義化的方式定義對象,然后使用 Array.filter 達到同樣的效果。

          const?fruits?=?[
          ??{?name:?'apple',?color:?'red'?},?
          ??{?name:?'strawberry',?color:?'red'?},?
          ??{?name:?'banana',?color:?'yellow'?},?
          ??{?name:?'pineapple',?color:?'yellow'?},?
          ??{?name:?'grape',?color:?'purple'?},?
          ??{?name:?'plum',?color:?'purple'?}
          ];

          function?pick(color)?{
          ??return?fruits.filter(f?=>?f.color?==?color);
          }

          使用數(shù)組新特性簡化邏輯判斷

          巧妙的利用 ES6 中提供的數(shù)組新特性,也可以讓我們更輕松的處理邏輯判斷。

          多條件判斷

          編碼時遇到多個判斷條件時,本能的寫下下面的代碼(其實也是最能表達業(yè)務(wù)邏輯的面向過程編碼)。

          function?judge(fruit)?{
          ??if?(fruit?===?'apple'?||?fruit?===?'strawberry'?||?fruit?===?'cherry'?||?fruit?===?'cranberries'?)?{
          ????console.log('red');
          ??}
          }

          但是當 type 未來到 10 種甚至更多時, 我們只能繼續(xù)添加 || 來維護代碼么?

          試試 Array.includes ~

          //?將判斷條件抽取成一個數(shù)組
          const?redFruits?=?['apple',?'strawberry',?'cherry',?'cranberries'];
          function?judge(type)?{
          ??if?(redFruits.includes(fruit))?{
          ????console.log('red');
          ??}
          }

          判斷數(shù)組中是否所有項都滿足某條件

          const?fruits?=?[
          ??{?name:?'apple',?color:?'red'?},
          ??{?name:?'banana',?color:?'yellow'?},
          ??{?name:?'grape',?color:?'purple'?}
          ];

          function?match()?{
          ??let?isAllRed?=?true;

          ??//?判斷條件:所有的水果都必須是紅色
          ??for?(let?f?of?fruits)?{
          ????if?(!isAllRed)?break;
          ????isAllRed?=?(f.color?===?'red');
          ??}

          ??console.log(isAllRed);?//?false
          }

          上面的實現(xiàn)中,主要是為了處理數(shù)組中的所有項都符合條件。

          使用 Array.every 可以很容的實現(xiàn)這個邏輯:

          const?fruits?=?[
          ??{?name:?'apple',?color:?'red'?},
          ??{?name:?'banana',?color:?'yellow'?},
          ??{?name:?'grape',?color:?'purple'?}
          ];

          function?match()?{
          ??//?條件:所有水果都必須是紅色
          ??const?isAllRed?=?fruits.every(f?=>?f.color?==?'red');

          ??console.log(isAllRed);?//?false
          }

          判斷數(shù)組中是否有某一項滿足條件

          Array.some,它主要處理的場景是判斷數(shù)組中是否有一項滿足條件。

          如果想知道是否有紅色水果,可以直接使用 Array.some 方法:

          const?fruits?=?[
          ????{?name:?'apple',?color:?'red'?},
          ????{?name:?'banana',?color:?'yellow'?},
          ????{?name:?'grape',?color:?'purple'?}
          ??];

          //?條件:是否有紅色水果?
          const?isAnyRed?=?fruits.some(f?=>?f.color?==?'red');

          還有許多其他數(shù)組新特性,比如 Array.find、Array.slice、Array.findIndex、Array.reduce、Array.splice 等,在實際場景中可以根據(jù)需要選擇使用。

          函數(shù)默認值

          使用默認參數(shù)

          const?buyFruit?=?(fruit,amount)?=>?{
          ??if(!fruit){
          ?? return
          ??}
          ??amount?=?amount?||?1;
          ??console.log(amount)
          }

          我們經(jīng)常需要處理函數(shù)內(nèi)部的一些參數(shù)默認值,上面的代碼大家都不陌生,使用函數(shù)的默認參數(shù),可以很好的幫助處理這種場景。

          const?buyFruit?=?(fruit,amount?=?1)?=>?{
          ??if(!fruit){
          ?? return
          ??}
          ??console.log(amount,'amount')
          }

          我們可以通過 Babel 的轉(zhuǎn)譯來看一下默認參數(shù)是如何實現(xiàn)的。

          從上面的轉(zhuǎn)譯結(jié)果可以發(fā)現(xiàn),只有參數(shù)為 undefined 時才會使用默認參數(shù)。

          測試的執(zhí)行結(jié)果如下:

          buyFruit('apple','');??//?amount
          buyFruit('apple',null);??//null?amount
          buyFruit('apple');??//1?amount

          所以使用默認參數(shù)的情況下,我們需要注意的是默認參數(shù) amount = 1 并不等同于 amount || 1

          使用解構(gòu)與默認參數(shù)

          當函數(shù)參數(shù)是對象時,我們可以使用解構(gòu)結(jié)合默認參數(shù)來簡化邏輯。

          Before:

          const?buyFruit?=?(fruit,amount)?=>?{
          ?fruit?=?fruit?||?{};
          ?if(!fruit.name?||?!fruit.price){
          ?? return;
          ?}
          ?...
          ??amount?=?amount?||?1;
          ??console.log(amount)
          }

          After:

          const?buyFruit?=?({?name,price?}={},amount)?=>?{
          ??if(!name?||?!prices){
          ??? return;
          ??}
          ??console.log(amount)
          }

          復(fù)雜數(shù)據(jù)解構(gòu)

          當處理比較簡的對象時,解構(gòu)與默認參數(shù)的配合是非常好的,但在一些復(fù)雜的場景中,我們面臨的可能是更復(fù)雜的結(jié)構(gòu)。

          const?oneComplexObj?=?{
          ?firstLevel: {
          ?? secondLevel:[{
          ??? name:"",
          ??? price:""
          ?? }]
          ? }
          }

          這個時候如果再通過解構(gòu)去獲取對象里的值。

          const?{
          ??firstLevel:{
          ????secondLevel:[{name,price]=[]
          ??}={}
          }?=?oneComplexObj;??????????????

          可讀性就會比較差,而且需要考慮多層解構(gòu)的默認值以及數(shù)據(jù)異常情況。

          這種情況下,如果項目中使用 lodash 庫,可以使用其中的 lodash/get 方法。

          import?lodashGet?from?'lodash/get';

          const?{?name,price}?=?lodashGet(oneComplexObj,'firstLevel.secondLevel[0]',{});

          策略模式優(yōu)化分支邏輯處理

          策略模式:定義一系列的算法,把它們一個個封裝起來, 并且使它們可相互替換。

          使用場景:策略模式屬于對象行為模式,當遇到具有相同行為接口、行為內(nèi)部不同邏輯實現(xiàn)的實例對象時,可以采用策略模式;或者是一組對象可以根據(jù)需要動態(tài)的選擇幾種行為中的某一種時,也可以采用策略模式;這里以第二種情況作為示例:

          Before:

          const?TYPE?=?{
          ??JUICE:'juice',
          ??SALAD:'salad',
          ??JAM:'jam'
          }
          function?enjoy({type?=?TYPE.JUICE,fruits}){
          ??if(!fruits?||?!fruits.length)?{
          ????console.log('請先采購水果!');
          ????return;
          ?}
          ??if(type?===?TYPE.JUICE)?{
          ????console.log('榨果汁中...');
          ????return?'果汁';
          ??}
          ??if(type?===?TYPE.SALAD)?{
          ????console.log('做沙拉中...');
          ????return?'拉沙';
          ??}
          ??if(type?===?TYPE.JAM)?{
          ????console.log('做果醬中...');
          ????return?'果醬';
          ??}
          ??return;
          }

          enjoy({type:'juice',fruits});

          使用思路:定義策略對象封裝不同行為、提供策略選擇接口,在不同的規(guī)則時調(diào)用相應(yīng)的行為。

          After:

          const?TYPE?=?{
          ??JUICE:'juice',
          ??SALAD:'salad',
          ??JAM:'jam'
          }

          const?strategies?=?{
          ??[TYPE.JUICE]:?function(fruits){
          ????console.log('榨果汁中...');
          ????return?'果汁';
          ??},
          ??[TYPE.SALAD]:function(fruits){
          ????console.log('做沙拉中...');
          ????return?'沙拉';
          ??},
          ??[TYPE.JAM]:function(fruits){
          ????console.log('做果醬中...');
          ????return?'果醬';
          ??},
          }

          function?enjoy({type?=?TYPE.JUICE,fruits})?{
          ??if(!type)?{
          ????console.log('請直接享用!');
          ????return;
          ?}
          ??if(!fruits?||?!fruits.length)?{
          ????console.log('請先采購水果!');
          ????return;
          ?}
          ???return?strategies[type](fruits);
          }

          enjoy({type:'juice',fruits});

          框架篇之 React JSX 邏輯判斷優(yōu)化

          JSX 是一個看起來很像 XML 的 JavaScript 語法擴展。一般在 React 中使用 JSX 來描述界面信息,ReactDOM.render() 將 JSX 界面信息渲染到頁面上。

          在 JSX 中支持 JavaScript 表達式,日常很常見的循環(huán)輸出子組件、三元表達式判斷、再復(fù)雜一些直接抽象出一個函數(shù)。

          在 JSX 中寫這么多 ?JavaScript 表達式,整體代碼看起來會有點兒雜亂。試著優(yōu)化一下!

          JSX-Control-Statements

          JSX-Control-Statements (https://www.npmjs.com/package/jsx-control-statements) 是一個 Babel 插件,它擴展了 JSX 的能力,支持以標簽的形式處理條件判斷、循環(huán)。

          If 標簽

          標簽內(nèi)容只有在 condition 為 true 時才會渲染,等價于最簡單的三元表達式。

          Before:

          {?condition()???'Hello?World!'?:?null?}???

          After:

          <If?condition={?condition()?}>Hello?World!If>???

          注意:已被廢棄,復(fù)雜的條件判斷可以使用標簽。

          Choose 標簽

          標簽下包括至少一個標簽、可選的標簽。

          標簽內(nèi)容只有在 condition 為 true 時才會渲染,相當于一個 if 條件判斷分支。

          標簽則相當于最后的 else 分支。

          Before:

          {?test1???<span>IfBlock1span>?:?test2???<span>IfBlock2span>?:?<span>ElseBlockspan>?}

          After:

          <Choose>
          ??<When?condition={?test1?}>
          ????<span>IfBlock1span>

          ??When>
          ??<When?condition={?test2?}>
          ????<span>IfBlock2span>

          ??When>
          ??<Otherwise>
          ????<span>ElseBlockspan>

          ??Otherwise>
          Choose>

          For 標簽

          標簽需要聲明 of、each 屬性。

          of 接收的是可以使用迭代器訪問的對象。

          each 代表迭代器訪問時的當前指向元素。

          Before:

          {
          ??(this.props.items?||?[]).map(item?=>?{
          ????return?<span?key={?item.id?}>{?item.title?}span>
          ??})
          }

          After:

          <For?each="item"?of={?this.props.items?}>
          ??<span?key={?item.id?}>{?item.title?}span>
          For>

          注意:標簽不能作為根元素。

          With 標簽

          標簽提供變量傳參的功能。

          Before:

          renderFoo?=?(foo)?=>?{
          ??return?<span>{?foo?}span>;
          }

          //?JSX?中表達式調(diào)用
          {
          ??this.renderFoo(47)
          }

          After:

          <With?foo={?47?}>
          ??<span>{?foo?}span>
          With>

          使用這幾種標簽優(yōu)化代碼,可以減少 ?JSX 中存在的顯式 JavaScript 表達式,使我們的代碼看上去更簡潔,但是這些標簽封裝的能力,在編譯時需要轉(zhuǎn)換為等價的 JavaScript 表達式。

          注意:具體 babel-plugin-jsx-control-statements 插件的使用見第三篇參考文章;Vue 框架已經(jīng)通過指令的形式支持 v-if、v-else-if、v-else、v-show、slot 等。

          總結(jié)

          以上我們總結(jié)了一些常見的邏輯判斷優(yōu)化技巧。當然,編寫高質(zhì)量可維護的代碼,除了邏輯判斷優(yōu)化,還需要有清晰的注釋、含義明確的變量命名、合理的代碼結(jié)構(gòu)拆分、邏輯分層解耦、以及更高層次的貼合業(yè)務(wù)的邏輯抽象等等,相信各位在這方面也有自己的一些心得,歡迎一起留言討論~

          參考文獻

          • 5 Tips to Write Better Conditionals in JavaScript (https://scotch.io/bar-talk/5-tips-to-write-better-conditionals-in-javascript)
          • stop-putting-so-many-if-statements-in-your-javascript (https://medium.com/better-programming/stop-putting-so-many-if-statements-in-your-javascript-3b65aaa4b86b)
          • JSX Control Statements (https://www.npmjs.com/package/jsx-control-statements)

          看完兩件事

          如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我兩件小事
          1.點個「在看」,讓更多人也能看到這篇內(nèi)容(點了在看」,bug -1 ?
          2.關(guān)注公眾號「前端公蝦米」,持續(xù)為你推送精選好文

          瀏覽 24
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  影音先锋自拍偷拍 | www.69自拍 | 久久久三级视频 | 热久久在线观看 | 99在线视频播放 |