资讯专栏INFORMATION COLUMN

JavaScript函数式编程之管道分支,消除if/else的一种方式

IntMain / 2439人阅读

摘要:在函数式编程中的错误处理,强壮代码文章中所用的思路与本篇一样,只不过在函数式编程中的错误处理,强壮代码中可以认为是以和作为标识,而本篇多带带创造了标识。使用本篇的方法重写函数式编程中的错误处理,强壮代码中的代码参考资料函数式编程指南我在

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

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

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

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
说到这里就已经很明显了,这种构造函数做标识的方式,把复杂的条件判断分解了,分解到在做判断的时候只需要选择方向,相关的操作可以扔到后面。

在《JavaScript函数式编程中的错误处理,强壮代码》文章中所用的思路与本篇一样,只不过在《JavaScript函数式编程中的错误处理,强壮代码》中可以认为是以nullundefined作为标识,而本篇多带带创造了标识。本篇中的方法更加的通用,因为nullundefined也可能是我们需要使用的值。

使用本篇的方法重写《JavaScript函数式编程中的错误处理,强壮代码》中的代码

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)
);

参考资料:

JS函数式编程指南

我在github https://github.com/zhuanyongx...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94281.html

相关文章

  • JavaScript函数编程入门经典

    摘要:函数式编程的定义函数是一段可以通过其名称被调用的代码。纯函数大多数函数式编程的好处来自于编写纯函数,纯函数是对给定的输入返回相同的输出的函数,并且纯函数不应依赖任何外部变量,也不应改变任何外部变量。 一个持续更新的github笔记,链接地址:Front-End-Basics,可以watch,也可以star。 此篇文章的地址:JavaScript函数式编程入门经典 正文开始 什么是函...

    silvertheo 评论0 收藏0
  • 编程函数编程

    摘要:声明式编程一种编程范式,与命令式编程相对立。常见的声明式编程语言有数据库查询语言,正则表达式逻辑编程函数式编程组态管理系统等。函数式编程,特别是纯函数式编程,尝试最小化状态带来的副作用,因此被认为是声明式的。 编程范式与函数式编程 一、编程范式的分类 常见的编程范式有:函数式编程、程序编程、面向对象编程、指令式编程等。在面向对象编程的世界,程序是一系列相互作用(方法)的对象(Class...

    noONE 评论0 收藏0
  • 编程 —— 函数编程入门

    摘要:在函数式编程中数据在由纯函数组成的管道中传递。函数式编程中函子是实现了函数的容器下文中将函子视为范畴,模型可表示如下但是在函数式编程中要避免使用这种面向对象的编程方式取而代之对外暴露了一个的接口也称为。 showImg(https://segmentfault.com/img/remote/1460000018101204); 该系列会有 3 篇文章,分别介绍什么是函数式编程、剖析函数...

    flyer_dev 评论0 收藏0
  • JavaScript 的未来:它还少些什么?

    摘要:例如通过哈希表映射需要一个操作来检查值是否相等,另一个操作用于创建哈希码。如果使用哈希码,则对象应该是不可变的。模式匹配提案目前处于第阶段。在本文,我们研究其中的智能管道另一个提议被称为。更强大,更重量级,并附带自己的数据结构。 翻译:疯狂的技术宅原文:http://2ality.com/2019/01/fut... 本文首发微信公众号:jingchengyideng欢迎关注,每天...

    layman 评论0 收藏0
  • 编写扁平化的代码

    摘要:原文作者给你的代码增加一点点函数式编程的特性最近我对函数式编程非常感兴趣。对我而言,函数式编程最大的作用就是强制你编写声明性代码代码描述你做什么,而不是在描述如何做。事实证明,编写声明式代码是函数式编程中最简单的部分之一。 原文:Writing flat & declarative code作者:Peeke Kuepers -- 给你的代码增加一点点函数式编程的特性 最近我对函数式编程...

    lunaticf 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<