1、前言

在之前探讨redux的中间件的时候,applyMiddleware源码中有遇到过compose()函数,当时不太明白起作用,所以就上网好好查了一下,做了个总结。

2、普通函数

在函数式编程当中有一个很重要的概念就是函数组合, 实际上就是把处理数据的函数像管道一样连接起来, 然后让数据穿过管道得到最终的结果。

例1:
const fn1 = (x) => x + 10;
const fn2 = (x) => x * 10;
const fn3 = (x) => x - 10;
console.log(fn3(fn2(fn1(1))))   ===>  100

我们想输出的是一个多层函数嵌套的运行结果,即把前一个函数的运行结果赋值给后一个函数。但是如果需要嵌套多层函数,那这种类似于f(g(h(x)))的写法可读性太差,我们考虑能不能写成(f, g, h)(x)这种简单直观的形式,于是compose()函数就正好帮助我们实现。

3、compose函数

3.1、compose的概念

先介绍一下compose()函数的概念

  • 概念:将需要嵌套执行的函数扁平化处理嵌套执行指的是,一个函数的返回值将作为另一个函数的参数
  • 作用:实现函数式编程中的 pointfree 风格(无参数),使我们专注于【转换】而不是【数据】
  • 实现:接收多个函数作为参数,从右到左,一个函数的输入为另一个函数的输出
  • 意义:编程更精练、算法更清晰、无参数干扰
  • 威力:【任意组合】
  • 缺点:不能直观的看到参数

3.2、compose的实现

首先,我们先看一下参数,funcs就是需要给出不确定个数的具体函数

function compose(...funcs) {//=>funcs:传递的函数集合
}

接着,我们看到compose函数执行后跟个(),说明外层函数执行完会再执行一个函数,即函数执行完会返回一个新函数,而且也会给出第一次调用函数时的参数

function compose(...funcs) {//=>funcs:传递的函数集合return function proxy(...args) { //=>args:第一次调用函数传递的参数集合}
}

紧接着,我们需要判断给出的函数集合的个数

  • 如果没有给函数,我们只需将后一个的参数返回;
  • 如果只给出一个函数,我们只需把后一个的参数赋给这个函数去执行即可:
function compose(...funcs) {//=>funcs:传递的函数集合return function proxy(...args) {//=>args:第一次调用函数传递的参数集合let len = funcs.length;if (len === 0) {//=>一个函数都不需要执行,直接返回参数argsreturn args;}if (len === 1) {//=>只需要执行第一个函数,把函数执行,把其结果返回即可return funcs[0](...args);}};
}
  • 如果给出的函数参数集合是两个及以上,那就是把前一个函数的执行结果赋给后一个函数。说到这,应该会想到一个满足这个需求的数组方法——reduce:
 function compose(...funcs) {//=>funcs:传递的函数集合return function proxy(...args) {//=>args:第一次调用函数传递的参数集合let len = funcs.length;if (len === 0) {//=>一个函数都不需要执行,直接返回参数argsreturn args;}if (len === 1) {//=>只需要执行第一个函数,把函数执行,把其结果返回即可return funcs[0](...args);}return funcs.reduce((x, y) => {//TODO});};
}

最后,我们需要注意的是,第一次执行的时候,参数x是个函数之后再执行的时候x是个函数执行的结果,所以需要进行判断:

function compose(...funcs) {//=>funcs:传递的函数集合return function proxy(...args) {//=>args:第一次调用函数传递的参数集合let len = funcs.length;if (len === 0) {//=>一个函数都不需要执行,直接返回ARGSreturn args;}if (len === 1) {//=>只需要执行一个函数,把函数执行,把其结果返回即可return funcs[0](...args);}return funcs.reduce((x, y) => {return typeof x === "function" ? y(x(...args)) : y(x)});};
}

这样,我们就完成了compose函数,我们将开始普通函数的案例改为compose函数:

unction compose(...funcs) {//=>funcs:传递的函数集合console.log('---funcs---',funcs)return function proxy(...args) {//=>args:第一次调用函数传递的参数集合let len = funcs.length;if (len === 0) {//=>一个函数都不需要执行,直接返回argsreturn args;}if (len === 1) {//=>只需要执行一个函数,把函数执行,把其结果返回即可return funcs[0](...args);}return funcs.reduce((x, y) => {console.log('--x--',x)console.log('--y--',y)return typeof x === "function" ? y(x(...args)) : y(x)});};
}
const fn1 = (x) => x + 10;
const fn2 = (x) => x * 10;
const fn3 = (x) => x - 10;
let result = compose(fn3, fn2, fn1)(1);
console.log('--result--',result);

  • 我们看出,第一次执行时,x是一个函数;第二次执行时,x是一个常数
  • 但是我们发现执行的结果是从左到右,即fn3 ——> fn2 ——> fn1,并不是我们期望的从右向左。所以我们可以使用reduceRight()或者reverse()帮助我们实现

我们将reduce改为reduceRightreverse()

function compose(...funcs) {//=>funcs:传递的函数集合console.log('---funcs---',funcs)return function proxy(...args) {//=>args:第一次调用函数传递的参数集合let len = funcs.length;if (len === 0) {//=>一个函数都不需要执行,直接返回argsreturn args;}if (len === 1) {//=>只需要执行一个函数,把函数执行,把其结果返回即可return funcs[0](...args);}//方式一return funcs.reduceRight((x, y) => {console.log('--x--',x)console.log('--y--',y)return typeof x === "function" ? y(x(...args)) : y(x)});//方式二return funcs.reverse().reduce((x, y) => {console.log('--x--',x)console.log('--y--',y)return typeof x === "function" ? y(x(...args)) : y(x)});};
}
const fn1 = (x) => x + 10;
const fn2 = (x) => x * 10;
const fn3 = (x) => x - 10;
let result = compose(fn3, fn2, fn1)(1);
console.log('--result--',result);

  • 这次,我们就实现了最初普通函数时这种(fn3(fn2(fn1(1))))嵌套逻辑,即利用compose函数compose(fn3, fn2, fn1)(1)

4、redux源码中的compose

在redux源码中compose有这样一种写法:

function compose(...funcs) {if (funcs.length === 0) {return arg => arg}if (funcs.length === 1) {return funcs[0]}return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

这种写法更简便,不用对第一个参数函数进行判断,与我们自己写compose函数的区别就是:x(y)还是y(x),这点我们要注意

同样,我们用刚才的案例测试redux源码中的compose函数:

function compose(...funcs) {if (funcs.length === 0) {return arg => arg}if (funcs.length === 1) {return funcs[0]}console.log('---funcs---',funcs)return funcs.reduce((a, b) => (console.log('--a--',a),console.log('--b--',b),(...args) => a(b(...args))))
}
const fn1 = (x) => x + 10;
const fn2 = (x) => x * 10;
const fn3 = (x) => x - 10;
let result = compose(fn3, fn2, fn1)(1);
console.log('--result--',result);


本文参考博客:

  • js高级编程技巧之——compose函数
  • JS - compose 代码组合
  • JS高阶编程技巧–COMPOSE函数

JS学习笔记——高级编程中compose函数的介绍和基本实现相关推荐

  1. 【theano-windows】学习笔记二——theano中的函数和共享参数

    前言 上一篇博客中学到了theano中的变量类型,也就是dscalar.dvector之类的, 然后还有一个theano.function和eval函数, 将我们所定义的操作转换成theano可执行的 ...

  2. js学习笔记——在html中嵌入脚本

    一.在html中嵌入js代码: 在html文件里嵌入js代码主要有四种形式: 第一种是通过<script></script>标记,这种一般用来定义一些函数,放在body外: 第 ...

  3. 3、Angular JS 学习笔记 – Controllers [翻译中]

    2019独角兽企业重金招聘Python工程师标准>>> 理解控制器 在Angular中,一个控制器是一个javascript构造函数用于填充Angular作用域. 当一个控制器通过使 ...

  4. python中argsort_(学习笔记)numpy中argsort函数用法

    在Python中使用help帮助 >>> import numpy >>> help(numpy.argsort) Help on function argsort ...

  5. JS学习笔记二 DOM,正则表达式简单例子介绍

    一.正则表达式 例子: var patt = /w3school/i; 例子解释: /w3school/i 是一个正则表达式. w3school 是模式(pattern)(在搜索中使用). i 是修饰 ...

  6. 数据库MySQL学习笔记高级篇(周阳)

    数据库MySQL学习笔记高级篇 1. mysql的架构介绍 mysql简介 高级Mysql mysqlLinux版的安装 mysql配置文件 mysql逻辑架构介绍 mysql存储引擎 2. 索引优化 ...

  7. JS学习笔记——入门基础知识总结

    JS入门基础知识总结1 前言 基础背景知识 一.产生历史: 二.特点: 三.应用方向: 四.Javascript组成: JavaScript书写使用方式 一.行内式(了解即可,项目中不使用,日常练习尽 ...

  8. JS学习笔记(五)函数类型、箭头函数、arguments参数、标签函数

    JS学习笔记(五) 本系列更多文章,可以查看专栏 JS学习笔记 文章目录 JS学习笔记(五) 一.函数 1. 函数定义 2. 方法( 对象 + 函数 ) 二.函数参数及返回值 1. 传递原始类型参数 ...

  9. python的messagebox的用法_Python GUI编程学习笔记之tkinter中messagebox、filedialog控件用法详解...

    本文实例讲述了Python GUI编程学习笔记之tkinter中messagebox.filedialog控件用法.分享给大家供大家参考,具体如下: 相关内容: messagebox 介绍 使用 fi ...

最新文章

  1. javascript之原型与原型链
  2. 从无到有算法养成篇-栈和队列·栈
  3. C++对象数组与对象指针的用法【C++初学面向对象编程】
  4. 的房费重构——上,下位机的复议
  5. Linux防火墙-netfilter-iptables
  6. 校园推广方案:常用手段及百试不爽的方法
  7. 云计算之路-阿里云上:2014年6月12日12点IIS请求到达量突降
  8. ipynb pycharm 运行_Mask RCNN代码之demo.ipynb运行与理解
  9. Verilog 层次化文件设计——彩灯控制器
  10. 安装SHARP MX-3618NC PCL6打印机驱动程序
  11. 拍拍贷2019Q1财报:核心用户转化率上升 迎战资本竞争力略显不足
  12. CSS3弹性布局、响应式布局、PS
  13. java 字符串 哈希值_Java 获取字符串Hash值
  14. 京交会将首设“一主多辅”场馆 展览面积较往届倍增
  15. 产品 • B端和C端产品经理有什么区别?
  16. Πολιτική απορρήτου
  17. 车牌识别 瞬间启动 快速抓拍 超低功耗 的4G智能摄像头方案
  18. 算法日志:python把终端的信息存为log和logging
  19. vba不能提取服务器上文件名,从全路径文件名中获取文件名(不含路径)
  20. 只有微信账号,我可以查询聊天记录吗?

热门文章

  1. SpringBoot——Bean管理
  2. Ansible(十七)-- ansible 中的循环(二) with_items with_list with_flattened with_together之间的区别和联系
  3. linux 内核 修改mss,Linux下TCP-MSS 修改,实验以及测试(详细)
  4. Ubuntu驱动安装闭坑
  5. 《Landmark Assisted CycleGAN for Cartoon Face Generation》人脸动漫化论文解析
  6. mask-rcnn报错: IndexError: boolean index did not match indexed array along dimension 0;...
  7. 相机标定的原理及四个坐标系之间的关系
  8. 自制 js 的 trim、right、left、instrRev、instr、mid 函数
  9. python网络爬虫工具库集合
  10. Buffer缓冲区的相关操作