JS学习笔记——高级编程中compose函数的介绍和基本实现
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
改为reduceRight
或reverse()
:
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函数的介绍和基本实现相关推荐
- 【theano-windows】学习笔记二——theano中的函数和共享参数
前言 上一篇博客中学到了theano中的变量类型,也就是dscalar.dvector之类的, 然后还有一个theano.function和eval函数, 将我们所定义的操作转换成theano可执行的 ...
- js学习笔记——在html中嵌入脚本
一.在html中嵌入js代码: 在html文件里嵌入js代码主要有四种形式: 第一种是通过<script></script>标记,这种一般用来定义一些函数,放在body外: 第 ...
- 3、Angular JS 学习笔记 – Controllers [翻译中]
2019独角兽企业重金招聘Python工程师标准>>> 理解控制器 在Angular中,一个控制器是一个javascript构造函数用于填充Angular作用域. 当一个控制器通过使 ...
- python中argsort_(学习笔记)numpy中argsort函数用法
在Python中使用help帮助 >>> import numpy >>> help(numpy.argsort) Help on function argsort ...
- JS学习笔记二 DOM,正则表达式简单例子介绍
一.正则表达式 例子: var patt = /w3school/i; 例子解释: /w3school/i 是一个正则表达式. w3school 是模式(pattern)(在搜索中使用). i 是修饰 ...
- 数据库MySQL学习笔记高级篇(周阳)
数据库MySQL学习笔记高级篇 1. mysql的架构介绍 mysql简介 高级Mysql mysqlLinux版的安装 mysql配置文件 mysql逻辑架构介绍 mysql存储引擎 2. 索引优化 ...
- JS学习笔记——入门基础知识总结
JS入门基础知识总结1 前言 基础背景知识 一.产生历史: 二.特点: 三.应用方向: 四.Javascript组成: JavaScript书写使用方式 一.行内式(了解即可,项目中不使用,日常练习尽 ...
- JS学习笔记(五)函数类型、箭头函数、arguments参数、标签函数
JS学习笔记(五) 本系列更多文章,可以查看专栏 JS学习笔记 文章目录 JS学习笔记(五) 一.函数 1. 函数定义 2. 方法( 对象 + 函数 ) 二.函数参数及返回值 1. 传递原始类型参数 ...
- python的messagebox的用法_Python GUI编程学习笔记之tkinter中messagebox、filedialog控件用法详解...
本文实例讲述了Python GUI编程学习笔记之tkinter中messagebox.filedialog控件用法.分享给大家供大家参考,具体如下: 相关内容: messagebox 介绍 使用 fi ...
最新文章
- javascript之原型与原型链
- 从无到有算法养成篇-栈和队列·栈
- C++对象数组与对象指针的用法【C++初学面向对象编程】
- 的房费重构——上,下位机的复议
- Linux防火墙-netfilter-iptables
- 校园推广方案:常用手段及百试不爽的方法
- 云计算之路-阿里云上:2014年6月12日12点IIS请求到达量突降
- ipynb pycharm 运行_Mask RCNN代码之demo.ipynb运行与理解
- Verilog 层次化文件设计——彩灯控制器
- 安装SHARP MX-3618NC PCL6打印机驱动程序
- 拍拍贷2019Q1财报:核心用户转化率上升 迎战资本竞争力略显不足
- CSS3弹性布局、响应式布局、PS
- java 字符串 哈希值_Java 获取字符串Hash值
- 京交会将首设“一主多辅”场馆 展览面积较往届倍增
- 产品 • B端和C端产品经理有什么区别?
- Πολιτική απορρήτου
- 车牌识别 瞬间启动 快速抓拍 超低功耗 的4G智能摄像头方案
- 算法日志:python把终端的信息存为log和logging
- vba不能提取服务器上文件名,从全路径文件名中获取文件名(不含路径)
- 只有微信账号,我可以查询聊天记录吗?
热门文章
- SpringBoot——Bean管理
- Ansible(十七)-- ansible 中的循环(二) with_items with_list with_flattened with_together之间的区别和联系
- linux 内核 修改mss,Linux下TCP-MSS 修改,实验以及测试(详细)
- Ubuntu驱动安装闭坑
- 《Landmark Assisted CycleGAN for Cartoon Face Generation》人脸动漫化论文解析
- mask-rcnn报错: IndexError: boolean index did not match indexed array along dimension 0;...
- 相机标定的原理及四个坐标系之间的关系
- 自制 js 的 trim、right、left、instrRev、instr、mid 函数
- python网络爬虫工具库集合
- Buffer缓冲区的相关操作