上一篇文章中我们介绍了函数柯里化,顺带提到了偏函数,接下来我们继续话题,进入今天的主题—函数的反柯里化。

在上一篇文章中柯里化函数你可能需要去敲许多代码,理解很多代码逻辑,不过这一节我们讨论的反科里化你可能不需要看很多代码逻辑,主要是理解反柯里化的核心思想,其实这种思想可能在你刚入门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高阶函数应用—函数柯里化和反柯里化(二)的更多相关文章

  1. JS高阶---变量与函数提升

    大纲: 主体: 案例1: 接下来在控制台source里进行断点测试 打好断点后,在控制台测试window .

  2. JS 函数的柯里化与反柯里化

    ===================================== 函数的柯里化与反柯里化 ===================================== [这是一篇比较久之前的总 ...

  3. JS高阶函数的理解(函数作为参数传递)

    JS高阶函数的理解 高阶函数是指至少满足下列条件之一的函数. · 函数可以作为参数被传递 · 函数可以作为返回值输出 一个例子,我们想在页面中创建100个div节点,这是一种写法.我们发现并不是所有用 ...

  4. React.js高阶函数的定义与使用

    /* 高阶函数的简单定义与使用 一: 先定义一个普通组件 二: 用function higherOrder(WrappendComponent) { return } 将组件包裹起来,并用export ...

  5. JS的防抖,节流,柯里化和反柯里化

    今天我们来搞一搞节流,防抖,柯里化和反柯里化吧,是不是一看这词就觉得哎哟wc,有点高大上啊.事实上,我们可以在不经意间用过他们但是你却不知道他们叫什么,没关系,相信看了今天的文章你会有一些收获的 节流 ...

  6. js高阶函数应用—函数柯里化和反柯里化

    在Lambda演算(一套数理逻辑的形式系统,具体我也没深入研究过)中有个小技巧:假如一个函数只能收一个参数,那么这个函数怎么实现加法呢,因为高阶函数是可以当参数传递和返回值的,所以问题就简化为:写一个 ...

  7. JS高阶函数与函数柯里化

    高阶函数 满足下列条件之一的函数: 函数作为参数被传递(如回调函数): 函数可以作为返回值输出: 一些内置高阶函数的例子: Array.prototype.map map()方法通过调用对输入数组中的 ...

  8. js 高阶函数 闭包

    摘自  https://www.cnblogs.com/bobodeboke/p/5594647.html 建议结合另外一篇关于闭包的文章一起阅读:http://www.cnblogs.com/bob ...

  9. js高阶函数应用—函数防抖和节流

    高阶函数指的是至少满足下列两个条件之一的函数: 1. 函数可以作为参数被传递:2.函数可以作为返回值输出: javaScript中的函数显然具备高级函数的特征,这使得函数运用更灵活,作为学习js必定会 ...

随机推荐

  1. php缩放gif和png图透明背景变成黑色的解决方法_php技巧

    工作中需要缩放一些gif图然后在去Imagecopymerge,可是发现使用了imagecreatetruecolor和imagecopyresampled后发现背景图不对,本来透明的背景图变成了黑色 ...

  2. JavaWeb学习笔记二 Http协议和Tomcat服务器

    Http协议 HTTP,超文本传输协议(HyperText Transfer Protocol),是互联网上应用最为广泛的一种网络协议.所有的WWW文件都必须遵守这个标准.设计HTTP最初的目的是为 ...

  3. Hibernate学习笔记一 使用idea开发工具搭建框架

    1.导包,包下载地址:http://hibernate.org/orm/downloads/ 2.创建数据库,准备表,实体.示例: CREATE TABLE `cst_customer` ( `cus ...

  4. java之静态属性和静态方法

    前言 静态属性和方法必须用static修饰符 静态属性和非静态属性的区别: 1.在内存中存放位置不同   所有带static修饰符的属性或者方法都存放在内存中的方法区  而非静态属性存放在内存中的堆区 ...

  5. 第五次作业-需求&原型改进

    需求&原型改进 0. 团队介绍 团队名称:121ComeOn 项目名称:个人博客项目 团队组成: PM:黄金筱(107) 成员:王枫(031),刘烨(255),周明浩(277) github地 ...

  6. 团队作业9——事后分析(Beta版本)

    事后诸葛亮分析 1.         总结 团队合照   a. 项目管理之事后诸葛亮会 ·设想和目标 (1)我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 个人学习 ...

  7. python第三方库requests详解

    Requests 是用Python语言编写,基于 urllib,采用 Apache2 Licensed 开源协议的 HTTP 库.它比 urllib 更加方便,可以节约我们大量的工作,完全满足 HTT ...

  8. Tornado websocket应用

    应用场景 WebSocket 的特点如下 适合服务器主动推送的场景(好友上线,即时聊天信息,火灾警告,股票涨停等) 相对于Ajax和Long poll等轮询技术,它更高效,不耗费网络带宽和计算资源 它 ...

  9. Spring-Data-JPA整合MySQL和配置

    一.简介 (1).MySQL是一个关系型数据库系统,是如今互联网公司最常用的数据库和最广泛的数据库.为服务端数据库,能承受高并发的访问量. (2).Spring-Data-Jpa是在JPA规范下提供的 ...

  10. Binary Tree Xorder Traversal

     * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeN ...