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...
本质上第一种方式也属于策略模式
// 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…