消灭IF ELSE的几种方式

2,951 阅读3分钟

1. 使用声名式验证代替命令式验证

// Nope
if (!data.name) {
    tip('请输入您的姓名')
} else if (!data.identify) {
    tip('请输入您的身份证')
} else if (!data.cardId) {
    tip('请输入您的借记卡号')
} else if (!data.bankSelect) {
    tip('请选择借记卡所属银行')
} else if (!data.cardTel) {
    tip('请输入您的预留手机号码')
} else {
    submitAuthentication()
}
// Yep
import Schema from 'async-validator'    

new Schema({
    name: { type: 'string', required: true, message: '请输入您的姓名' },
    identify: { type: 'string', required: true, message: '请输入您的身份证' },
    cardId: { type: 'string', required: true, message: '请输入您的借记卡号' },
    bankSelect: { type: 'object', required: true, message: '请选择借记卡所属银行' },
    cardTel: { type: 'string', required: true, message: '请输入您的预留手机号码' }
}).validate(dataToCheck).then(() => {
    that.submitAuthentication()
}).catch(({ errors, fields }) => {
    // errors是所有错误的数组,
    // fields是所有验证错误的字段为key,相应的错误为value
    console.log(errors)
    // 仅提示第一个错误信息,
    // 这里建议把错误信息及时反馈到表单上的每个字段上
    tip(errors[0].message)
})

// 异步判断
{
    name: { type: 'string', required: true, message: '请输入您的姓名' },
    identify: [
        { type: 'string', required: true, message: '请输入您的身份证' },
        { asyncValidator(rule, value, callback, source, options) {
              api.check(someData).then((data) => {
                data.valid ? callback() : callback(new Error(data.message))
              }).catch(error => {
                callback(new Error(error))
              })
        }}
    ],
    cardId: { type: 'string', required: true, message: '请输入您的借记卡号' },
    bankSelect: { type: 'object', required: true, message: '请选择借记卡所属银行' },
    cardTel: { type: 'string', required: true, message: '请输入您的预留手机号码' }
}

async-validator: github.com/yiminghe/as…

2. 使用策略模式替代 if...if...if...

本质上第一种方式也属于策略模式

参考:juejin.cn/post/684490…

// Nope
const aCase = 'case1'

if(aCase === 'case1') {
  // do sth  
}

if(aCase === 'case2') {
  // do sth  
}

if(aCase === 'case3') {
  // do sth  
}

if(aCase === 'case4') {
  // do sth  
}
// Better, but Nope
switch (status){
case 1:
  sendLog('processing')
  jumpTo('IndexPage')
  break
case 2:
case 3:
  sendLog('fail')
  jumpTo('FailPage')
  break  
case 4:
  sendLog('success')
  jumpTo('SuccessPage')
  break
case 5:
  sendLog('cancel')
  jumpTo('CancelPage')
  break
default:
  sendLog('other')
  jumpTo('Index')
  break
}
// Much better, Yep
const aCase = 'case1'

// 基本类型作为key
new Map([
  ['case1',()=>{/*do sth*/}],
  ['case2',()=>{/*do sth*/}],
  //...
]).get(aCase)()


// object作为key
const actions = new Map([
  [{identity:'guest',status:1},()=>{/* functionA */}],
  [{identity:'guest',status:2},()=>{/* functionA */}],
  [{identity:'guest',status:3},()=>{/* functionA */}],
  [{identity:'guest',status:4},()=>{/* functionA */}],
  [{identity:'guest',status:5},()=>{/* functionB */}],
  //...
])

// 正则用法
const actions = ()=>{
  const functionA = ()=>{/*do sth*/}
  const functionB = ()=>{/*do sth*/}
  const functionC = ()=>{/*send log*/}
  return new Map([
    [/^guest_[1-4]$/,functionA],
    [/^guest_5$/,functionB],
    [/^guest_.*$/,functionC],
    //...
  ])
}

const onButtonClick = (identity,status)=>{
  // 过滤某些策略
  let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`)))
  action.forEach(([key,value])=>value.call(this))
}

Map的key可以是任意类型的数据

3. 使用责任链模式替代 if / else,if / else, ...

// Nope
// 温度为0时输出水结冰了
if(temp === 0) {
    return 'water freezes at 0°C' 
// 温度为100时输出
} else if(temp === 100) {
    return 'water boils at 100°C'
} else { 
    return 'nothing special happens at ' + temp + '°C'
}

使用函数式编程库Ramda:

// Yep
const fn = R.cond([
  [R.equals(0),   R.always('water freezes at 0°C')],
  [R.equals(100), R.always('water boils at 100°C')],
  [R.T,           temp => 'nothing special happens at ' + temp + '°C']
]);
fn(0); //=> 'water freezes at 0°C'
fn(50); //=> 'nothing special happens at 50°C'
fn(100); //=> 'water boils at 100°C'
  • 一定程度上,Map的正则用法也可以实现责任链模式,不过Ramda更简洁。
  • 值得一提的是,Ramda还有许多非常实用的用法,让我们的代码更简洁,个人认为本质上是更趋向语义化
  • 尝试用Ramda提供的函数来作为你代码中最基本的元素,而不是用最原生JS
  • 当然,物极必反,不必追求处处都使用函数式编程,合适永远是不错的

Ramda: ramda.cn/docs/#cond

关于函数式编程

函数式编程的本质是Pointfree的概念,细读这篇文章便能明白为什么函数式编程会出现

Pointfree: www.ruanyifeng.com/blog/2017/0…