什么是柯里化(currying)?

维基百科中的解释是:柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。意思就是当函数被调用时,返回的函数还需要设置一些传入的参数。

首先来看一个简单的例子,有下面一个函数:

function add(num1, num2) {
return num1 + num2;
}

我们把它改写成下面这样:

var fn = function(a) {
return function (b) {
return a + b;
}
}

可以这样调用函数:fn(2)(3)。上面使用了匿名函数来实现多参数函数的方法,虽然这并不是柯里化的函数,但可以帮助我们理解柯里化的含义。

实现通用柯里化函数###

我们可以在内置构造函数Function()的原型上来添加一个柯里化函数,这样所有的函数都可以调用。下面是通用柯里化函数的实现:

Function.prototype.currying = function () {
var that = this;
var args = [].slice.call(arguments);
return function () {
return that.apply(null, args.concat([].slice.call(arguments)));
}
}

现在用柯里化函数将上面的add函数柯里化:

var curriedAdd = add.currying(2);
curriedAdd(3); // 5

也可以一次性传入两个参数:

var curriedAdd = add.currying(2, 3);
curriedAdd(); // 5

我们知道在原生对象的原型上扩展方法是不太好的,因为可能会导致命名冲突。所以最好不要把currying函数扩展在Function的原型上,下面是改写的currying函数:

function currying(fn) {
var args = [].slice.call(arguments, 1);
return function () {
return that.apply(null, args.concat([].slice.call(arguments)));
}
}

改写之后currying函数的第一个参数是要被柯里化的函数,可以这样调用:

var curriedAdd = currying(add, 2);
curriedAdd(3); // 5

var curriedAdd = currying(add, 2, 3);
curriedAdd(); // 5

上面的add函数只是两个数字的相加,如果我们需要n个数字相加,上面的currying函数已经不能满足要求了,下面是修改后的currying函数:

function currying(fn) {
var argsArr = [];
return function () {
if (arguments.length === 0) {
return fn.apply(null, argsArr);
} else {
[].push.apply(argsArr, arguments);
}
}
}

多个数字相加:

var add = function () {
var num = 0;
[].forEach.call(arguments, function (item, i) {
num += item;
})
return num;
} var curriedAdd = currying(add);
curriedAdd(2);
curriedAdd(3);
curriedAdd(4);
curriedAdd(5);
curriedAdd(); // 14

这样做有什么好处呢?假如说我们只想知道这个月花了多少钱,而中间的某一天之前花了多少我们并不想知道,我们只在乎结果,不在乎过程,上面的currying函数很好地解决了这个问题。有的人说这样做可以节省性能,我倒觉得这和性能没多大关系,或者说这样做的目的并不是为了性能,因为每次计算结果和最后一起计算结果是一样的,都是要计算一样的次数。还有一个好处就是可以复用currying函数,比如我们要多个数字相乘或者其他操作,都可以用currying函数,处理数字只需修改fn参数就可以。

说到柯里化就不得不说Function.prototype.bind这个方法了,它也实现了函数的柯里化。我们可以自己来实现一个bind函数:

function bind(fn, context) {
var args = [].slice.call(arguments, 2);
return function () {
return fn.apply(context, args.concat([].slice.call(arguments)));
}
}

假如我们需要改变fn中的this上下文,就可以用bind函数,否则可以用currying函数。

JavaScript之函数柯里化的更多相关文章

  1. 简单粗暴详细讲解javascript实现函数柯里化与反柯里化

    函数柯里化(黑人问号脸)???Currying(黑人问号脸)???妥妥的中式翻译既视感:下面来一起看看究竟什么是函数柯里化: 维基百科的解释是:把接收多个参数的函数变换成接收一个单一参数(最初函数的第 ...

  2. 简单粗暴详细讲解javascript实现函数柯里化

    函数柯里化(黑人问号脸)???Currying(黑人问号脸)???妥妥的中式翻译既视感:下面来一起看看究竟什么是函数柯里化: 维基百科的解释是:把接收多个参数的函数变换成接收一个单一参数(最初函数的第 ...

  3. Javascript函数柯里化(curry)

    函数柯里化currying,是函数式编程非常重要的一个标志.它的实现需要满足以下条件,首先就是函数可以作为参数进行传递,然后就是函数可以作为返回值return出去.我们依靠这个特性编写很多优雅酷炫的代 ...

  4. 深入理解javascript函数进阶系列第二篇——函数柯里化

    前面的话 函数柯里化currying的概念最早由俄国数学家Moses Schönfinkel发明,而后由著名的数理逻辑学家Haskell Curry将其丰富和发展,currying由此得名.本文将详细 ...

  5. JavaScript中的事件循环机制跟函数柯里化

    一.事件循环机制的理解 test();//按秒输出5个5 function test() { for (var i = 0; i < 5; i++) { setTimeout(() => ...

  6. JavaScript函数柯里化的一些思考

    1. 高阶函数的坑 在学习柯里化之前,我们首先来看下面一段代码: var f1 = function(x){ return f(x); }; f1(x); 很多同学都能看出来,这些写是非常傻的,因为函 ...

  7. 一道javascript面试题(闭包与函数柯里化)

    要求写一个函数add(),分别实现能如下效果: (1)console.log(add(1)(2)(3)(4)()); (2)console.log(add(1,2)(3,4)()); (3)conso ...

  8. 精读JavaScript模式(六),Memoization模式与函数柯里化的应用

    假期就这么结束了!十天假就有三天在路上,真的难受!想想假期除了看了两场电影貌似也没做什么深刻印象的事情.流浪地球,特效还是很赞,不过对于感情的描写还是逃不掉拖沓和尴尬的通病,对于国产科幻还是抱有支持的 ...

  9. JavaScript中的函数柯里化与反柯里化

    一.柯里化定义 在计算机科学中,柯里化是把 接受多个参数的函数 变换成 接受一个单一参数(最初函数的第一个参数)的函数 并且返回 接受余下参数且返回结果的新函数的技术 高阶函数 高阶函数是实现柯里化的 ...

随机推荐

  1. iOS之UIButton扩大按钮的响应区域

    在开发中有时会遇见设计图里按钮设计的特别小,这时会用到手动扩大UIButton的响应范围,下面有两个解决办法: 第一种方法:创建一个类目:UIButton+EnlargeTouchArea .h文件 ...

  2. 仿手机QQ消息小红点动画2

    前言 上一篇把动画的实现步骤大致理清,需要确认小尾巴的绘制区域,关键就是确定4个顶点的位置.大家可以根据需要,选择不同的计算方式. 今天,要实现: 文字的添加 尾巴拉长用弧形代替直线 下面是现在的效果 ...

  3. 一次tomcat的调优记录

    项目本身需要上传模型,使用的是springboot1.5.3. 上传的模型比较大,下载的过程中就出现了问题(下载是su调用的java接口,其开发并非本人负责,不可更改) 问题在于,下载的时候tomca ...

  4. mysql千万级数据量根据索引优化查询速度

    (一)索引的作用 索引通俗来讲就相当于书的目录,当我们根据条件查询的时候,没有索引,便需要全表扫描,数据量少还可以,一旦数据量超过百万甚至千万,一条查询sql执行往往需要几十秒甚至更多,5秒以上就已经 ...

  5. Python实现创建字典

    编写一个名为 make_album() 的函数,它创建一个描述音乐专辑的字典.1.这个函数应接受歌手的名字和专辑名,并返回一个包含这两项信息的字典.使用这个函数创建三个表示不同专辑的字典,并打印每个返 ...

  6. Spring retry实践

    在开发中,重试是一个经常使用的手段.比如MQ发送消息失败,会采取重试手段,比如工程中使用RPC请求外部服务,可能因为网络波动出现超时而采取重试手段......可以看见重试操作是非常常见的一种处理问题, ...

  7. MySQL数据库实验:任务一 创建数据库和表

    目录 任务一 创建数据库和表 [实训目的与要求] [实训原理] [实训步骤] 一.熟悉MySQL环境 二.利用MySQL命令行窗口创建数据库及表 三.利用界面工具创建数据库及表 任务一 创建数据库和表 ...

  8. Python学习 :反射 & 单例模式

    反射 什么是反射? - 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省) 面向对象中的反射 - 通过字符串的形式来操作(获取.检查.增加.删除)对象中的成员 - python中的 ...

  9. python教程(七)·字典

    本文介绍本系列教程最后一个数据结构--字典 在现实生活中,查英语字典的时候,我们通常根据单词来查找意思.而python中的字典也是类似的,根据特定的 "键"(单词)来查找 &quo ...

  10. uni-app 下拉至指定高度固定view

    uni.createSelectorQuery().select(‘#salyt’).boundingClientRect(function(rects){ console.log(rects) va ...