js高阶函数应用—函数柯里化和反柯里化(二)
上一篇文章中我们介绍了函数柯里化,顺带提到了偏函数,接下来我们继续话题,进入今天的主题—函数的反柯里化。
在上一篇文章中柯里化函数你可能需要去敲许多代码,理解很多代码逻辑,不过这一节我们讨论的反科里化你可能不需要看很多代码逻辑,主要是理解反柯里化的核心思想,其实这种思想可能在你刚入门js时候就接触到了,而且你几乎天天在写代码过程中使用它。
首先需要理解反柯里化我们先来回顾上一节的内容,科里化简单概括一下就是我们做了这样一件事情:把接受多个参数的函数变换成接受一个单一参数的函数,并且返回新的函数来执行余下的数,公式表示基本模式为fn(a,b,c,d)=>fn(a)(b)(c)(d)。
那么很容易类比过来,反柯里化就是fn(a)(b)(c)(d)=>fn(a,b,c,d)是这样吗?其实就是这样,不要怀疑就是这么简单,只不过实现过程没有这么直接明了而已,反柯里化大概是做了这么一件事情:把已经内置的特定使用场景的函数通过参数解放出来,提高函数的适用范围。
转化为公式:
curyyA=fn(a);
curryA(b)(c)(d)=>fn(a,b,c,d);
或者
curyyAb=fn(a)(b);//或者curyyAb=fn(a,b);
curryAb(c)(d)=>fn(a,b,c,d);
......以此类推
为了方便理解我们把上一节中的curry函数add版本拿过来
const curry = (fn, ...arg) => {
let all = arg || [],
length = fn.length;
return (...rest) => {
let _args = all.slice();
_args.push(...rest);
if (_args.length < length) {
return curry.call(this, fn, ..._args);
} else {
return fn.apply(this, _args);
}
}
}
const add = curry((a, b) => a + b);
const add6 = add();
console.log(add6()); //
console.log(add6()); //
可以看到我们柯里化后的函数,每次执行了后返回的函数对于应用场景针对性越强,例如这个add6就是任意一个数和6的和,比起原来的add可以实现任意两数的和,add6应用范围更窄了,不过add6这个应用场景更有针对性,例如我们就需要一个任意数与6的和的时候这个add6就适应我们的场景。
这样应该很容易理解了吧,如果还有问题我们来看一个更简单的例子:
let getTag = (type) => {
return `小明是一个${type}`
}
getTag('好学生');//小明是一个好学生
getTag('好老师');//小明是一个好老师
这个getTag函数根据传入的参数返回小明的类型“小明是一个xxx”,不过这个函数是我们在专门得到小明的类型用的,如果我们现在有个需求需要的到“小华是一个xxx”,你总不会再写一个函数
getTagHua = (type) => `小华是一个${type}`
来得到“小华是一个xxx”的结果吧,就算刚入门编程语言时候我们也不会这么写;要实现这个需求很简单,再传入一个参数就行了
let getTag2 = (name, type) => `${name}是一个${type}`
getTag2('小华', '好学生')//小华是个好学生
到这里我们可以对比一下getTag和getTag2这两个函数,我们发现:
1.getTag是一个getTag2柯里化后带着一个“小明”的内置参数的版本
2.getTag只针对得到小明类型的场景使用
3.getTag2是一个getTag泛化后的版本,适用范围更广
可以理解为getTag2是getTag的反柯里化函数,是不是很简单,反柯里化的函数编程思想我们天天在用,只是没注意到而已。现在再回去看之前的公式
curyyA=fn(a);
curryA(b)(c)(d)=>fn(a,b,c,d);
这种类型的转化是不是就很容易理解了。
接下来我们接续我们的话题,我们现在知道了,反柯里化是一种编程思想,通过解析出函数内部限定条件,然后把限定条件当做参数传给函数,从而提高函数使用范围的一种编程思想,既然这样我们就很容易理解我们下面这种情况了
class Book {
constructor(name) {
this.name = name;
}
static declare() {
console.log('A Study Method');
}
sayName() {
console.log(this.name);
}
}
class Blog {
constructor(name) {
this.name = name;
}
}
let book = new Book('javacript语言精粹');
let blog = new Blog('博客园');
book.sayName(); //javacript语言精粹
Book.prototype.sayName.call(blog); //博客园
Book.declare.call(blog); //A Study Method
如上面我们在开发经常遇到的,blog想用book类的方法,用call和apply改变一下this指向就行了,我们常用的call,apply函数本身就是反科里化思想的体现,把函数的调用对象当做参数传给函数,从而提高函数的适用范围,类似于做了这么一件事情:
obj.fn(a,b)=>fn(object,a,b) 的转化
通过转化使得fn不再只适用于obj调用还可以让其他的object调用,提高其适用范围
那么我们把这个转化过程用函数实现一下
Function.prototype.uncurrying = function() { //外边这个不要写成箭头函数,因为我们具体反柯里化什么函数是我们调用时候才知道的
return (obj, ...rest) => this.apply(obj, rest);
}
let sayName = Book.prototype.sayName.uncurrying();
let deClare = Book.declare.uncurrying();
sayName(blog) //博客园
deClare(blog) //A Study Method
deClare(book) //A Study Method
可以看到我们把Book类才能调用的静态方法declare,和book实例的sayName反柯里化后,这种只针对类名调用的方法和只针对Book类对象调用的方法可以让其他对象调用了。
当然我们也可以把一些js内置对象的方法uncurrying一下,比如一个字符串‘sdjkfjksfsdkslkdjsdf’,我们想把它每一个字符都拆出来放到一个数组中,或者每一项都拼个固定的字符再返回一数组,我们可以吧Array里的map方法uncurrying一下:
let map = Array.prototype.map.uncurrying();
console.log(map('yuweryiweryuie', val => val + 'test'))

当然很多js内置对象的方法可以uncurrying的,这里不做过多介绍,因为都是我们常用的call,apply的场景,即一些具有类似数据结构或者相同迭代器的对象我们通常会借用其他的对象方法
我们这里还给出一种上述uncurrying的实现方式:
Function.prototype.uncurrying = function() {
return (...rest) => Function.prototype.call.apply(this, rest);
}
如果你不是很理解代码也没关系,没太大影响,因为这个实现只是把之前的
Function.prototype.uncurrying = function() { //外边这个不要写成箭头函数,因为我们具体反柯里化什么函数是我们调用时候才知道的
return (obj, ...rest) => this.apply(obj, rest);
}
这个函数的传obj的这个工作交给call来完成了,如果你还是不理解建议去mdn上看看call和apply用法,理一下逻辑就行,这里不做过多阐释,好了我们currying和uncurrying的内容就到这了。
js高阶函数应用—函数柯里化和反柯里化(二)的更多相关文章
- JS高阶---变量与函数提升
大纲: 主体: 案例1: 接下来在控制台source里进行断点测试 打好断点后,在控制台测试window .
- JS 函数的柯里化与反柯里化
===================================== 函数的柯里化与反柯里化 ===================================== [这是一篇比较久之前的总 ...
- JS高阶函数的理解(函数作为参数传递)
JS高阶函数的理解 高阶函数是指至少满足下列条件之一的函数. · 函数可以作为参数被传递 · 函数可以作为返回值输出 一个例子,我们想在页面中创建100个div节点,这是一种写法.我们发现并不是所有用 ...
- React.js高阶函数的定义与使用
/* 高阶函数的简单定义与使用 一: 先定义一个普通组件 二: 用function higherOrder(WrappendComponent) { return } 将组件包裹起来,并用export ...
- JS的防抖,节流,柯里化和反柯里化
今天我们来搞一搞节流,防抖,柯里化和反柯里化吧,是不是一看这词就觉得哎哟wc,有点高大上啊.事实上,我们可以在不经意间用过他们但是你却不知道他们叫什么,没关系,相信看了今天的文章你会有一些收获的 节流 ...
- js高阶函数应用—函数柯里化和反柯里化
在Lambda演算(一套数理逻辑的形式系统,具体我也没深入研究过)中有个小技巧:假如一个函数只能收一个参数,那么这个函数怎么实现加法呢,因为高阶函数是可以当参数传递和返回值的,所以问题就简化为:写一个 ...
- JS高阶函数与函数柯里化
高阶函数 满足下列条件之一的函数: 函数作为参数被传递(如回调函数): 函数可以作为返回值输出: 一些内置高阶函数的例子: Array.prototype.map map()方法通过调用对输入数组中的 ...
- js 高阶函数 闭包
摘自 https://www.cnblogs.com/bobodeboke/p/5594647.html 建议结合另外一篇关于闭包的文章一起阅读:http://www.cnblogs.com/bob ...
- js高阶函数应用—函数防抖和节流
高阶函数指的是至少满足下列两个条件之一的函数: 1. 函数可以作为参数被传递:2.函数可以作为返回值输出: javaScript中的函数显然具备高级函数的特征,这使得函数运用更灵活,作为学习js必定会 ...
随机推荐
- NSRC技术分享——自制Linux Rootkit检测工具
### 前言 Linux系统中存在用户态与内核态,当用户态的进程需要申请某些系统资源时便会发起系统调用.而内核态如何将系统的相关信息实时反馈给用户态呢,便是通过proc文件系统.如此便营造了一个相对隔 ...
- Linux下的指令:find,which
1. 在Linux系统下,使用find来查找文件: find [path] [condition] [operation] path指定了在哪个目录查找,condition限定了查找条件,operat ...
- IDEA配置Struts框架
对于刚接触编程的同学,对框架只是还不是很了解,本文主要介绍在Idea上配置Struts,实现简单的页面跳转,以及页面参数传递. 在进行代码编写之前先对Idea进行一个简单了解,对于长时间接触编程的,对 ...
- Alpha第四天
Alpha第四天 听说 031502543 周龙荣(队长) 031502615 李家鹏 031502632 伍晨薇 031502637 张柽 031502639 郑秦 1.前言 任务分配是VV.ZQ. ...
- 实验三《Java面向对象程序设计》实验报告
20162308 实验三<Java面向对象程序设计>实验报告 实验内容 XP基础 XP核心实践 IDEA工具学习 密码学算法基础 实验步骤 (一)Refactor/Reformat使用 p ...
- 1013团队Beta冲刺day3
项目进展 李明皇 今天解决的进度 完善了程序的运行逻辑(消息提示框等) 明天安排 前后端联动调试 林翔 今天解决的进度 向微信官方申请登录验证session以维护登录态 明天安排 继续完成维护登录态 ...
- 2018C程序设计—第0次作业
1.翻阅邹欣老师博客关于师生关系博客,并回答下列问题,每个问题的答案不少于500字 1)最理想的师生关系是健身教练和学员的关系,在这种师生关系中你期望获得来自老师的哪些帮助? 答:正如邹欣老师博客里所 ...
- 结合Socket实现DDoS攻击
一.实验说明 1. 实验介绍 通过上一节实验的SYN泛洪攻击结合Socket实现DDoS攻击. 2. 开发环境 Ubuntu Linux Python 3.x版本 3. 知识点 本次实验将涉及以下知识 ...
- bzoj千题计划275:bzoj4817: [Sdoi2017]树点涂色
http://www.lydsy.com/JudgeOnline/problem.php?id=4817 lct+线段树+dfs序 操作1:access 操作2:u到根的-v到根的-lca到根的*2+ ...
- api-gateway实践(13)新服务网关 - 断路保护/熔断机制
参考链接:SpringCloud的Hystrix(五) Hystrix机制 新需求列表 1.在线测试 根据定义,生成输入界面, 点击测试, 验证参数,发起调用,返回执行结果 2.熔断保护 两个实现类: ...