这是第 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