這是第 64 篇不摻水的原創,想獲取更多原創好文,請搜索公衆號關注咱們吧~ 本文首發於政採雲前端博客:編寫高質量可維護的代碼:邏輯判斷javascript
if else、switch case 是平常開發中最多見的條件判斷語句,這種看似簡單的語句,當遇到複雜的業務場景時,若是處理不善,就會出現大量的邏輯嵌套,可讀性差而且難以擴展。前端
編寫高質量可維護的代碼,咱們先從最小處入手,一塊兒來看看在前端開發過程當中,能夠從哪些方面來優化邏輯判斷?java
下面咱們會分別從 JavaScript 語法和 React JSX 語法兩個方面來分享一些優化的技巧。算法
function supply(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
// 條件 1: 水果存在
if (fruit) {
// 條件 2: 屬於紅色水果
if (redFruits.includes(fruit)) {
console.log('紅色水果');
// 條件 3: 水果數量大於 10 個
if (quantity > 10) {
console.log('數量大於 10 個');
}
}
} else {
throw new Error('沒有水果啦!');
}
}
複製代碼
分析上面的條件判斷,存在三層 if 條件嵌套。npm
若是提早 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: 水果數量大於 10 個
if (quantity > 10) {
console.log('數量大於 10 個');
}
}
複製代碼
當須要枚舉值處理不一樣的業務分支邏輯時, 第一反應是寫下 if else ?咱們來看一下:babel
function pick(color) {
// 根據顏色選擇水果
if(color === 'red') {
return ['apple', 'strawberry'];
} else if (color === 'yellow') {
return ['banana', 'pineapple'];
} else if (color === 'purple') {
return ['grape', 'plum'];
} else {
return [];
}
}
複製代碼
在上面的實現中:markdown
使用 switch case 優化上面的代碼後:數據結構
function pick(color) {
// 根據顏色選擇水果
switch (color) {
case 'red':
return ['apple', 'strawberry'];
case 'yellow':
return ['banana', 'pineapple'];
case 'purple':
return ['grape', 'plum'];
default:
return [];
}
}
複製代碼
switch case 優化以後的代碼看上去格式整齊,思路很清晰,但仍是很冗長。繼續優化:app
const fruitColor = {
red: ['apple', 'strawberry'],
yellow: ['banana', 'pineapple'],
purple: ['grape', 'plum'],
};
function pick(color) {
return fruitColor[color] || [];
}
複製代碼
const fruitColor = new Map()
.set('red', ['apple', 'strawberry'])
.set('yellow', ['banana', 'pineapple'])
.set('purple', ['grape', 'plum']);
function pick(color) {
return fruitColor.get(color) || [];
}
複製代碼
優化以後,代碼更簡潔、更容易擴展。
爲了更好的可讀性,還能夠經過更加語義化的方式定義對象,而後使用 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);
}
複製代碼
巧妙的利用 ES6 中提供的數組新特性,也可讓咱們更輕鬆的處理邏輯判斷。
編碼時遇到多個判斷條件時,本能的寫下下面的代碼(其實也是最能表達業務邏輯的面向過程編碼)。
function judge(fruit) {
if (fruit === 'apple' || fruit === 'strawberry' || fruit === 'cherry' || fruit === 'cranberries' ) {
console.log('red');
}
}
複製代碼
可是當 type 將來到 10 種甚至更多時, 咱們只能繼續添加 || 來維護代碼麼 ?
試試 Array.includes ~
// 將判斷條件抽取成一個數組
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
function judge(fruit) {
if (redFruits.includes(fruit)) {
console.log('red');
}
}
複製代碼
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
}
複製代碼
上面的實現中,主要是爲了處理數組中的全部項都符合條件。
使用 Array.every 能夠很容的實現這個邏輯:
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
}
複製代碼
Array.some ,它主要處理的場景是判斷數組中是否有一項知足條件。
若是想知道是否有紅色水果,能夠直接使用 Array.some 方法:
const fruits = [
{ name: 'apple', color: 'red' },
{ name: 'banana', color: 'yellow' },
{ name: 'grape', color: 'purple' }
];
// 條件:是否有紅色水果
const isAnyRed = fruits.some(f => f.color == 'red');
複製代碼
還有許多其餘數組新特性,好比 Array.find、Array.slice、Array.findIndex、Array.reduce、Array.splice 等,在實際場景中能夠根據須要選擇使用。
const buyFruit = (fruit,amount) => {
if(!fruit){
return
}
amount = amount || 1;
console.log(amount)
}
複製代碼
咱們常常須要處理函數內部的一些參數默認值,上面的代碼你們都不陌生,使用函數的默認參數,能夠很好的幫助處理這種場景。
const buyFruit = (fruit,amount = 1) => {
if(!fruit){
return
}
console.log(amount,'amount')
}
複製代碼
咱們能夠經過 Babel 的轉譯來看一下默認參數是如何實現的。
從上面的轉譯結果能夠發現,只有參數爲 undefined 時纔會使用默認參數。
測試的執行結果以下:
buyFruit('apple',''); // amount
buyFruit('apple',null); //null amount
buyFruit('apple'); //1 amount
複製代碼
因此使用默認參數的狀況下,咱們須要注意的是默認參數 amount=1
並不等同於 amount || 1
。
當函數參數是對象時,咱們可使用解構結合默認參數來簡化邏輯。
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)
}
複製代碼
當處理比較簡的對象時,解構與默認參數的配合是很是好的,但在一些複雜的場景中,咱們面臨的多是更復雜的結構。
const oneComplexObj = {
firstLevel: {
secondLevel: [{
name: '',
price: '',
}],
},
};
複製代碼
這個時候若是再經過解構去獲取對象裏的值。
const {
firstLevel: {
secondLevel: [{ name, price }] = [],
} = {},
} = oneComplexObj;
複製代碼
可讀性就會比較差,並且須要考慮多層解構的默認值以及數據異常狀況。
這種狀況下,若是項目中使用 lodash 庫,可使用其中的 lodash/get 方法。
import lodashGet from 'lodash/get';
const { name,price } = lodashGet(oneComplexObj,'firstLevel.secondLevel[0]',{});
複製代碼
策略模式:定義一系列的算法,把它們一個個封裝起來, 而且使它們可相互替換。
使用場景:策略模式屬於對象行爲模式,當遇到具備相同行爲接口、行爲內部不一樣邏輯實現的實例對象時,能夠採用策略模式;或者是一組對象能夠根據須要動態的選擇幾種行爲中的某一種時,也能夠採用策略模式;這裏以第二種狀況做爲示例:
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 '果醬';
}
}
enjoy({ type: 'juice', fruits });
複製代碼
使用思路:定義策略對象封裝不一樣行爲、提供策略選擇接口,在不一樣的規則時調用相應的行爲。
After:
const TYPE = {
JUICE: 'juice',
SALAD: 'salad',
JAM: 'jam',
};
const strategies = {
[TYPE.JUICE](fruits) {
console.log('榨果汁中...');
return '果汁';
},
[TYPE.SALAD](fruits) {
console.log('作沙拉中...');
return '沙拉';
},
[TYPE.JAM](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 });
複製代碼
JSX 是一個看起來很像 XML 的 JavaScript 語法擴展。通常在 React 中使用 JSX 來描述界面信息,ReactDOM.render() 將 JSX 界面信息渲染到頁面上。
在 JSX 中支持 JavaScript 表達式,平常很常見的循環輸出子組件、三元表達式判斷、再複雜一些直接抽象出一個函數。
在 JSX 中寫這麼多 JavaScript 表達式,總體代碼看起來會有點兒雜亂。試着優化一下!
JSX-Control-Statements 是一個 Babel 插件,它擴展了 JSX 的能力,支持以標籤的形式處理條件判斷、循環。
標籤內容只有在 condition 爲 true 時纔會渲染,等價於最簡單的三元表達式。
Before:
{ condition() ? 'Hello World!' : null }
複製代碼
After:
<If condition={ condition() }>Hello World!</If>
複製代碼
注意: 已被廢棄,複雜的條件判斷可使用 標籤。
標籤下包括至少一個 標籤、可選的 標籤。
標籤內容只有在 condition 爲 true 時纔會渲染,至關於一個 if 條件判斷分支。
標籤則至關於最後的 else 分支。
Before:
{ test1 ? <span>IfBlock1</span> : test2 ? <span>IfBlock2</span> : <span>ElseBlock</span> }
複製代碼
After:
<Choose>
<When condition={ test1 }> <span>IfBlock1</span> </When>
<When condition={ test2 }> <span>IfBlock2</span> </When>
<Otherwise> <span>ElseBlock</span> </Otherwise>
</Choose>
複製代碼
標籤須要聲明 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>
複製代碼
注意: 標籤不能做爲根元素。
標籤提供變量傳參的功能。
Before:
renderFoo = (foo) => {
return <span>{ foo }</span>;
}
// JSX 中表達式調用
{
this.renderFoo(47)
}
複製代碼
After:
<With foo={ 47 }>
<span>{ foo }</span>
</With>
複製代碼
使用這幾種標籤優化代碼,能夠減小 JSX 中存在的顯式 JavaScript 表達式,使咱們的代碼看上去更簡潔,可是這些標籤封裝的能力,在編譯時須要轉換爲等價的 JavaScript 表達式。
注意:具體 babel-plugin-jsx-control-statements 插件的使用見第三篇參考文章;Vue 框架已經經過指令的形式支持 v-if、v-else-if、v-else、v-show、slot 等。
以上咱們總結了一些常見的邏輯判斷優化技巧。固然,編寫高質量可維護的代碼,除了邏輯判斷優化,還須要有清晰的註釋、含義明確的變量命名、合理的代碼結構拆分、邏輯分層解耦、以及更高層次的貼合業務的邏輯抽象等等,相信各位在這方面也有本身的一些心得,歡迎一塊兒留言討論~
政採雲前端團隊(ZooTeam),一個年輕富有激情和創造力的前端團隊,隸屬於政採雲產品研發部,Base 在風景如畫的杭州。團隊現有 50 餘個前端小夥伴,平均年齡 27 歲,近 3 成是全棧工程師,妥妥的青年風暴團。成員構成既有來自於阿里、網易的「老」兵,也有浙大、中科大、杭電等校的應屆新人。團隊在平常的業務對接以外,還在物料體系、工程平臺、搭建平臺、性能體驗、雲端應用、數據分析及可視化等方向進行技術探索和實戰,推進並落地了一系列的內部技術產品,持續探索前端技術體系的新邊界。
若是你想改變一直被事折騰,但願開始能折騰事;若是你想改變一直被告誡須要多些想法,卻無從破局;若是你想改變你有能力去作成那個結果,卻不須要你;若是你想改變你想作成的事須要一個團隊去支撐,但沒你帶人的位置;若是你想改變既定的節奏,將會是「 5 年工做時間 3 年工做經驗」;若是你想改變原本悟性不錯,但老是有那一層窗戶紙的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但願參與到隨着業務騰飛的過程,親手推進一個有着深刻的業務理解、完善的技術體系、技術創造價值、影響力外溢的前端團隊的成長曆程,我以爲咱們該聊聊。任什麼時候間,等着你寫點什麼,發給 ZooTeam@cai-inc.com