博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript函数式编程之管道分支,消除if/else的一种方式
阅读量:5894 次
发布时间:2019-06-19

本文共 3434 字,大约阅读时间需要 11 分钟。

以下代码会用到函数组合函数compose,只要知道compose是干什么的就足够了,如果好奇具体的实现,可以看

管道是函数式编程中经常使用的,很多时候我们需要按照条件判断进行组合函数的选择,简单的说就是从原来的一条管道变成两条管道,根据判断选择进入哪一条。

这里的关键在于,我们如何判断上一个函数的返回值应该进入哪一条管道?

let step1 = x => x ? 1 : 2;let step2 = x => x === 1 ? 3 : 4;let step3 = x => x === 3 ? 5 : 6;let getResult = compose(step3, step2, step1)let result = getResult(1);

这是最直接的方法,每一步根据返回值单独判断,如果step1的返回值发生了变化,下一步的判断也需要跟着修改,这种写法显然不好。那我们能不能在返回值的基础上加上一个标识,专门用来做判断?我们很自然的就会想到对象。

let step1 = x => {  if (x) {    return {      value: 1,      identity: 'channelOne'    }  } else {    return {      value: 2,      identity: 'channelTwo'    }  }}let step2 = x => {  if (x.identity === 'channelOne') {    return x.value = 3;  } else {    return x.value = 4;  }}let step3 = x => {  if (x.identity === 'channelOne') {    return x.value = 5;  } else {    return x.value = 6;  }    }let getResult = compose(step3, step2, step1);let result = getResult(1);

是不是好了很多?不过这依然要继续改进。当我们需要使用forin等方式遍历对象时,identity会被遍历出来,一般情况下我们都希望它不会被遍历,那就还需要把这个属性定义为不可枚举的。

修改step1并简化代码:

let step1 = x => {  if (x) {    let obj = {value: 1};    Object.defineProperty(obj, 'identity', {      enumerable: false,      value: 'channelOne'    });    return obj;  } else {    let obj = {value: 2};    Object.defineProperty(obj, 'identity', {      enumerable: false,      value: 'channelTwo'    });    return obj;  }}let selectChannel = (fn1, fn2) => val => val.identity === 'channelOne' ? fn1(val) : fn2(val);let getResult = compose(  selectChannel(    x => Object.defineProperty(x, 'value', {value: 5}),    x => Object.defineProperty(x, 'value', {value: 6})  ),  selectChannel(    x => Object.defineProperty(x, 'value', {value: 3}),    x => Object.defineProperty(x, 'value', {value: 4})  ),  step1);let result = getResult(1);

在selectChannel中,函数会根据传进来的对象的标识选择执行。至此,功能基本上实现了,可依然不够好,代码不够简洁优雅,重用也可以继续改进。

用构造函数做标识

let channelOne = function(x) { this.value = x; };channelOne.of = x => new channelOne(x);let channelTwo = function(x) { this.value = x; };channelTwo.of = x => new channelTwo(x);let step1 = x => x ? channelOne.of(1) : channelTwo.of(2);let selectChannel = (fn1, fn2) => val => val.constructor === channelOne ? fn1(val) : fn2(val);let getResult = compose(  selectChannel(x => channelOne.of(5), x => channelTwo.of(6)),  selectChannel(x => channelOne.of(3), x => channelTwo.of(4)),  step1);let result = getResult(1);

太棒了!

看到这里,有么有惊喜的发现,if/else不见了?肯定会有人觉得我是一个换汤不换药的奸商。虽然if/else不见了,可是我用了三元运算符,这在本质上有什么区别?
答案是,没区别,他们都是条件判断,这是不可避免的。
我们不妨暂时把关注的焦点放在三元运算符与if/else的区别上面来。我们什么时候会使用三元运算符?是条件判断很简单的时候,简单到只需要一个表达式,而不是复杂的操作。虽然三元运算符也可以用逗号隔开表达式从而进行多个操作,可我们这个时候更愿意使用if/else
说到这里就已经很明显了,这种构造函数做标识的方式,把复杂的条件判断分解了,分解到在做判断的时候只需要选择方向,相关的操作可以扔到后面。

在《》文章中所用的思路与本篇一样,只不过在《》中可以认为是以nullundefined作为标识,而本篇单独创造了标识。本篇中的方法更加的通用,因为nullundefined也可能是我们需要使用的值。

使用本篇的方法重写《》中的代码

let channelError = function(x) { this.value = x; };channelError.of = x => new channelError(x);let channelSuccess = function(x) { this.value = x; };channelSuccess.of = x => new channelSuccess(x);let security= fn => val => val.constructor === channelError? val : fn(val);let validate1 = x => x ? channelSuccess.of(x) : channelError.of('validate1 is not passed!');let validate2 = x => x.value ? x : channelError.of('validate2 is not passed!');let handleError = x => {  if (x.constructor === channelError) alert(x.value);};let postData = () => axios.post(...);let getResult = compose(  handleError,  security(postData),  security(validate2),  security(validate1));

参考资料:

我在github

转载地址:http://ttisx.baihongyu.com/

你可能感兴趣的文章
css的border的solid
查看>>
div+css实现window xp桌面图标布局(至上而下从左往右)
查看>>
0-1 背包问题
查看>>
运行Maven是报错:No goals have been specified for this build
查看>>
Haskell 差点儿无痛苦上手指南
查看>>
[MODx] Build a CMP (Custom manager page) using MIGX in MODX 2.3 -- 1
查看>>
NTP 服务器配置
查看>>
jQuery自动完成点击html元素
查看>>
[算法]基于分区最近点算法的二维平面
查看>>
linux在文件打包和压缩
查看>>
Angular - - ngList、ngRepeat、ngModelOptions
查看>>
[LeetCode136]Single Number寻找一个数组里只出现一次的数
查看>>
webpack多页应用架构系列(七):开发环境、生产环境傻傻分不清楚?
查看>>
bootstrap - image
查看>>
spring-boot 和 webpack-dev-server联合开发
查看>>
从TimSort说起
查看>>
构建 iOS 界面:子类化 Views
查看>>
笨办法学C 练习1:启用编译器
查看>>
用Golang写一个搜索引擎(0x01)--- 基本概念
查看>>
【算法之美】logn 时间复杂度求解两个有序数组的中位数
查看>>